voice handling in moderated groups (#788)
This commit is contained in:
parent
51a2372869
commit
2631a9bdba
|
@ -149,11 +149,13 @@ public class InputFieldStatus : Object {
|
|||
public string? message;
|
||||
public MessageType message_type;
|
||||
public InputState input_state;
|
||||
public bool contains_markup;
|
||||
|
||||
public InputFieldStatus(string? message, MessageType message_type, InputState input_state) {
|
||||
public InputFieldStatus(string? message, MessageType message_type, InputState input_state, bool contains_markup = false) {
|
||||
this.message = message;
|
||||
this.message_type = message_type;
|
||||
this.input_state = input_state;
|
||||
this.contains_markup = contains_markup;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ public class MucManager : StreamInteractionModule, Object {
|
|||
public signal void room_info_updated(Account account, Jid muc_jid);
|
||||
public signal void private_room_occupant_updated(Account account, Jid room, Jid occupant);
|
||||
public signal void invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason);
|
||||
public signal void voice_request_received(Account account, Jid room_jid, Jid from_jid, string? nick, string? role, string? label);
|
||||
public signal void received_occupant_role(Account account, Jid jid, Xep.Muc.Role? role);
|
||||
public signal void bookmarks_updated(Account account, Set<Conference> conferences);
|
||||
public signal void conference_added(Account account, Conference conference);
|
||||
public signal void conference_removed(Account account, Jid jid);
|
||||
|
@ -118,6 +120,16 @@ public class MucManager : StreamInteractionModule, Object {
|
|||
if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, jid.bare_jid, nick, role);
|
||||
}
|
||||
|
||||
public void change_role(Account account, Jid jid, string nick, string role) {
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).change_role(stream, jid.bare_jid, nick, role);
|
||||
}
|
||||
|
||||
public void request_voice(Account account, Jid jid) {
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).request_voice(stream, jid.bare_jid);
|
||||
}
|
||||
|
||||
public bool kick_possible(Account account, Jid occupant) {
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
if (stream != null) return stream.get_module(Xep.Muc.Module.IDENTITY).kick_possible(stream, occupant);
|
||||
|
@ -137,6 +149,18 @@ public class MucManager : StreamInteractionModule, Object {
|
|||
return flag.has_room_feature(jid, Xep.Muc.Feature.NON_ANONYMOUS) && flag.has_room_feature(jid, Xep.Muc.Feature.MEMBERS_ONLY);
|
||||
}
|
||||
|
||||
public bool is_moderated_room(Account account, Jid jid) {
|
||||
XmppStream? stream = stream_interactor.get_stream(account);
|
||||
if (stream == null) {
|
||||
return false;
|
||||
}
|
||||
Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY);
|
||||
if (flag == null) {
|
||||
return false;
|
||||
}
|
||||
return flag.has_room_feature(jid, Xep.Muc.Feature.MODERATED);
|
||||
}
|
||||
|
||||
public bool is_public_room(Account account, Jid jid) {
|
||||
return is_groupchat(jid, account) && !is_private_room(account, jid);
|
||||
}
|
||||
|
@ -285,6 +309,12 @@ public class MucManager : StreamInteractionModule, Object {
|
|||
stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).invite_received.connect( (stream, room_jid, from_jid, password, reason) => {
|
||||
invite_received(account, room_jid, from_jid, password, reason);
|
||||
});
|
||||
stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).voice_request_received.connect( (stream, room_jid, from_jid, nick, role, label) => {
|
||||
voice_request_received(account, room_jid, from_jid, nick, role, label);
|
||||
});
|
||||
stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).received_occupant_role.connect( (stream, from_jid, role) => {
|
||||
received_occupant_role(account, from_jid, role);
|
||||
});
|
||||
stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).room_info_updated.connect( (stream, muc_jid) => {
|
||||
room_info_updated(account, muc_jid);
|
||||
});
|
||||
|
|
|
@ -13,6 +13,7 @@ public class NotificationEvents : StreamInteractionModule, Object {
|
|||
public signal void notify_subscription_request(Conversation conversation);
|
||||
public signal void notify_connection_error(Account account, ConnectionManager.ConnectionError error);
|
||||
public signal void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string? password, string? reason);
|
||||
public signal void notify_voice_request(Account account, Jid room_jid, Jid from_jid, string? nick, string? role, string? label);
|
||||
|
||||
private StreamInteractor stream_interactor;
|
||||
|
||||
|
@ -27,6 +28,7 @@ public class NotificationEvents : StreamInteractionModule, Object {
|
|||
stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(on_content_item_received);
|
||||
stream_interactor.get_module(PresenceManager.IDENTITY).received_subscription_request.connect(on_received_subscription_request);
|
||||
stream_interactor.get_module(MucManager.IDENTITY).invite_received.connect((account, room_jid, from_jid, password, reason) => notify_muc_invite(account, room_jid, from_jid, password, reason));
|
||||
stream_interactor.get_module(MucManager.IDENTITY).voice_request_received.connect((account, room_jid, from_jid, nick, role, label) => notify_voice_request(account, room_jid, from_jid, nick, role, label));
|
||||
stream_interactor.connection_manager.connection_error.connect((account, error) => notify_connection_error(account, error));
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,7 @@ SOURCES
|
|||
|
||||
src/ui/contact_details/blocking_provider.vala
|
||||
src/ui/contact_details/settings_provider.vala
|
||||
src/ui/contact_details/permissions_provider.vala
|
||||
src/ui/contact_details/dialog.vala
|
||||
src/ui/contact_details/muc_config_form_provider.vala
|
||||
|
||||
|
|
|
@ -138,6 +138,14 @@ public class Dino.Ui.Application : Gtk.Application, Dino.Application {
|
|||
});
|
||||
add_action(accept_muc_invite_action);
|
||||
|
||||
SimpleAction accept_voice_request_action = new SimpleAction("accept-voice-request", VariantType.INT32);
|
||||
accept_voice_request_action.activate.connect((variant) => {
|
||||
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32());
|
||||
if (conversation == null) return;
|
||||
stream_interactor.get_module(MucManager.IDENTITY).change_role(conversation.account, conversation.counterpart, conversation.nickname, "participant");
|
||||
});
|
||||
add_action(accept_voice_request_action);
|
||||
|
||||
SimpleAction loop_conversations_action = new SimpleAction("loop_conversations", null);
|
||||
loop_conversations_action.activate.connect(() => { window.loop_conversations(false); });
|
||||
add_action(loop_conversations_action);
|
||||
|
|
|
@ -5,6 +5,7 @@ using Gtk;
|
|||
using Dino.Entities;
|
||||
|
||||
namespace Dino.Ui {
|
||||
private const string OPEN_CONVERSATION_DETAILS_URI = "x-dino:open-conversation-details";
|
||||
|
||||
public class ChatInputController : Object {
|
||||
|
||||
|
@ -38,8 +39,19 @@ public class ChatInputController : Object {
|
|||
chat_text_view_controller.send_text.connect(send_text);
|
||||
|
||||
chat_input.encryption_widget.encryption_changed.connect(on_encryption_changed);
|
||||
|
||||
|
||||
chat_input.file_button.clicked.connect(() => file_picker_selected());
|
||||
|
||||
stream_interactor.get_module(MucManager.IDENTITY).received_occupant_role.connect(update_moderated_input_status);
|
||||
stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect(update_moderated_input_status);
|
||||
|
||||
status_description_label.activate_link.connect((uri) => {
|
||||
if (uri == OPEN_CONVERSATION_DETAILS_URI){
|
||||
ContactDetails.Dialog contact_details_dialog = new ContactDetails.Dialog(stream_interactor, conversation);
|
||||
contact_details_dialog.present();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void set_conversation(Conversation conversation) {
|
||||
|
@ -51,6 +63,10 @@ public class ChatInputController : Object {
|
|||
|
||||
chat_input.initialize_for_conversation(conversation);
|
||||
chat_text_view_controller.initialize_for_conversation(conversation);
|
||||
|
||||
Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
|
||||
|
||||
update_moderated_input_status(conversation.account, own_jid);
|
||||
}
|
||||
|
||||
public void set_file_upload_active(bool active) {
|
||||
|
@ -69,6 +85,10 @@ public class ChatInputController : Object {
|
|||
input_field_status = status;
|
||||
|
||||
chat_input.set_input_state(status.message_type);
|
||||
|
||||
if (status.contains_markup) status_description_label.use_markup = true;
|
||||
else status_description_label.use_markup = false;
|
||||
|
||||
status_description_label.label = status.message;
|
||||
|
||||
chat_input.file_button.sensitive = status.input_state == Plugins.InputFieldStatus.InputState.NORMAL;
|
||||
|
@ -147,6 +167,19 @@ public class ChatInputController : Object {
|
|||
stream_interactor.get_module(ChatInteraction.IDENTITY).on_message_cleared(conversation);
|
||||
}
|
||||
}
|
||||
|
||||
private void update_moderated_input_status(Account account, Xmpp.Jid jid) {
|
||||
Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
|
||||
if (conversation.type_ == conversation.Type.GROUPCHAT){
|
||||
if (stream_interactor.get_module(MucManager.IDENTITY).is_moderated_room(conversation.account, conversation.counterpart) &&
|
||||
stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account)==Xmpp.Xep.Muc.Role.VISITOR) {
|
||||
set_input_field_status(new Plugins.InputFieldStatus(_("This conference does not allow you to send messages. %s").printf("<a href=\"" + OPEN_CONVERSATION_DETAILS_URI + "\">" + _("Request permission") + "</a>"),
|
||||
Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND, true));
|
||||
} else {
|
||||
reset_input_field_status();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool on_text_input_key_press(EventKey event) {
|
||||
if (event.keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") {
|
||||
|
|
|
@ -46,6 +46,7 @@ public class Dialog : Gtk.Dialog {
|
|||
app.plugin_registry.register_contact_details_entry(new SettingsProvider(stream_interactor));
|
||||
app.plugin_registry.register_contact_details_entry(new BlockingProvider(stream_interactor));
|
||||
app.plugin_registry.register_contact_details_entry(new MucConfigFormProvider(stream_interactor));
|
||||
app.plugin_registry.register_contact_details_entry(new PermissionsProvider(stream_interactor));
|
||||
|
||||
foreach (Plugins.ContactDetailsProvider provider in app.plugin_registry.contact_details_entries) {
|
||||
provider.populate(conversation, contact_details, Plugins.WidgetType.GTK);
|
||||
|
|
29
main/src/ui/contact_details/permissions_provider.vala
Normal file
29
main/src/ui/contact_details/permissions_provider.vala
Normal file
|
@ -0,0 +1,29 @@
|
|||
using Gtk;
|
||||
|
||||
using Dino.Entities;
|
||||
|
||||
namespace Dino.Ui.ContactDetails {
|
||||
|
||||
public class PermissionsProvider : Plugins.ContactDetailsProvider, Object {
|
||||
public string id { get { return "permissions"; } }
|
||||
|
||||
private StreamInteractor stream_interactor;
|
||||
|
||||
public PermissionsProvider(StreamInteractor stream_interactor) {
|
||||
this.stream_interactor = stream_interactor;
|
||||
}
|
||||
|
||||
public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) {
|
||||
if (type != Plugins.WidgetType.GTK) return;
|
||||
|
||||
Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account);
|
||||
if (stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account)==Xmpp.Xep.Muc.Role.VISITOR){
|
||||
Button voice_request = new Button() {visible=true, label=_("Request")};
|
||||
voice_request.clicked.connect(()=>stream_interactor.get_module(MucManager.IDENTITY).request_voice(conversation.account, conversation.counterpart));
|
||||
contact_details.add(_("Permissions"), _("Request permission to send messages"), "", voice_request);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -45,6 +45,7 @@ public class SettingsProvider : Plugins.ContactDetailsProvider, Object {
|
|||
combobox.append("on", get_notify_setting_string(Conversation.NotifySetting.ON));
|
||||
combobox.append("off", get_notify_setting_string(Conversation.NotifySetting.OFF));
|
||||
contact_details.add(DETAILS_HEADLINE_ROOM, _("Notifications"), "", combobox);
|
||||
|
||||
combobox.active_id = get_notify_setting_id(conversation.notify_setting);
|
||||
combobox.changed.connect(() => { conversation.notify_setting = get_notify_setting(combobox.active_id); } );
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ public class Notifications : Object {
|
|||
stream_interactor.get_module(NotificationEvents.IDENTITY).notify_subscription_request.connect(notify_subscription_request);
|
||||
stream_interactor.get_module(NotificationEvents.IDENTITY).notify_connection_error.connect(notify_connection_error);
|
||||
stream_interactor.get_module(NotificationEvents.IDENTITY).notify_muc_invite.connect(on_invite_received);
|
||||
stream_interactor.get_module(NotificationEvents.IDENTITY).notify_voice_request.connect(on_voice_request_received);
|
||||
}
|
||||
|
||||
private async void notify_content_item(ContentItem content_item, Conversation conversation) {
|
||||
|
@ -140,6 +141,28 @@ public class Notifications : Object {
|
|||
notification.add_button_with_target_value(_("Accept"), "app.open-muc-join", group_conversation.id);
|
||||
GLib.Application.get_default().send_notification(null, notification);
|
||||
}
|
||||
|
||||
private async void on_voice_request_received(Account account, Jid room_jid, Jid from_jid, string? nick, string? role, string? label) {
|
||||
Conversation? direct_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(room_jid, account, Conversation.Type.GROUPCHAT);
|
||||
if (direct_conversation == null) return;
|
||||
string display_name = Util.get_participant_display_name(stream_interactor, direct_conversation, from_jid);
|
||||
string display_room = room_jid.bare_jid.to_string();
|
||||
Notification notification = new Notification(_("Permission request"));
|
||||
string body = _("%s requests the permission to write in %s").printf(display_name, display_room);
|
||||
notification.set_body(body);
|
||||
|
||||
try {
|
||||
Cairo.ImageSurface jid_avatar = (yield Util.get_conversation_avatar_drawer(stream_interactor, direct_conversation)).size(40, 40).draw_image_surface();
|
||||
notification.set_icon(get_pixbuf_icon(jid_avatar));
|
||||
} catch (Error e) { }
|
||||
|
||||
Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT);
|
||||
group_conversation.nickname = nick;
|
||||
notification.set_default_action_and_target_value("app.accept-voice-request", new Variant.int32(group_conversation.id));
|
||||
notification.add_button_with_target_value(_("Deny"), "app.deny-voice-request", group_conversation.id);
|
||||
notification.add_button_with_target_value(_("Accept"), "app.accept-voice-request", group_conversation.id);
|
||||
GLib.Application.get_default().send_notification(null, notification);
|
||||
}
|
||||
|
||||
private Icon get_pixbuf_icon(Cairo.ImageSurface surface) throws Error {
|
||||
Gdk.Pixbuf avatar = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height());
|
||||
|
|
|
@ -97,6 +97,21 @@ public class View : Popover {
|
|||
outer_box.add(kick_button);
|
||||
kick_button.clicked.connect(kick_button_clicked);
|
||||
}
|
||||
if (stream_interactor.get_module(MucManager.IDENTITY).is_moderated_room(conversation.account, conversation.counterpart) && role == Xmpp.Xep.Muc.Role.MODERATOR){
|
||||
if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR) {
|
||||
ModelButton voice_button = new ModelButton() { active=true, text=_("Grant write permission"), visible=true };
|
||||
outer_box.add(voice_button);
|
||||
voice_button.clicked.connect(() =>
|
||||
voice_button_clicked("participant"));
|
||||
}
|
||||
else if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.PARTICIPANT){
|
||||
ModelButton voice_button = new ModelButton() { active=true, text=_("Revoke write permission"), visible=true };
|
||||
outer_box.add(voice_button);
|
||||
voice_button.clicked.connect(() =>
|
||||
voice_button_clicked("visitor"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (jid_menu != null) jid_menu.destroy();
|
||||
stack.add_named(outer_box, "menu");
|
||||
|
@ -119,6 +134,12 @@ public class View : Popover {
|
|||
|
||||
stream_interactor.get_module(MucManager.IDENTITY).kick(conversation.account, conversation.counterpart, selected_jid.resourcepart);
|
||||
}
|
||||
|
||||
private void voice_button_clicked(string role) {
|
||||
if (selected_jid == null) return;
|
||||
|
||||
stream_interactor.get_module(MucManager.IDENTITY).change_role(conversation.account, conversation.counterpart, selected_jid.resourcepart, role);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ public class DataForm {
|
|||
public ListSingleField(StanzaNode node) {
|
||||
base.from_node(node);
|
||||
type_ = Type.LIST_SINGLE;
|
||||
node.set_attribute("type", "list-single");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ private const string NS_URI = "http://jabber.org/protocol/muc";
|
|||
private const string NS_URI_ADMIN = NS_URI + "#admin";
|
||||
private const string NS_URI_OWNER = NS_URI + "#owner";
|
||||
private const string NS_URI_USER = NS_URI + "#user";
|
||||
private const string NS_URI_REQUEST = NS_URI + "#request";
|
||||
|
||||
public enum MucEnterError {
|
||||
NONE,
|
||||
|
@ -68,6 +69,7 @@ public class Module : XmppStreamModule {
|
|||
public signal void received_occupant_role(XmppStream stream, Jid jid, Role? role);
|
||||
public signal void subject_set(XmppStream stream, string? subject, Jid jid);
|
||||
public signal void invite_received(XmppStream stream, Jid room_jid, Jid from_jid, string? password, string? reason);
|
||||
public signal void voice_request_received(XmppStream stream, Jid room_jid, Jid from_jid, string? nick, string? role, string? label);
|
||||
public signal void room_info_updated(XmppStream stream, Jid muc_jid);
|
||||
|
||||
public signal void self_removed_from_room(XmppStream stream, Jid jid, StatusCode code);
|
||||
|
@ -154,6 +156,25 @@ public class Module : XmppStreamModule {
|
|||
stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, message);
|
||||
}
|
||||
|
||||
public void request_voice(XmppStream stream, Jid to_muc) {
|
||||
MessageStanza message = new MessageStanza() { to=to_muc };
|
||||
|
||||
DataForms.DataForm submit_node = new DataForms.DataForm();
|
||||
submit_node.get_submit_node();
|
||||
|
||||
DataForms.DataForm.Field field_node = new DataForms.DataForm.Field() { var="FORM_TYPE" };
|
||||
field_node.set_value_string(NS_URI_REQUEST);
|
||||
|
||||
DataForms.DataForm.ListSingleField single_field = new DataForms.DataForm.ListSingleField(new StanzaNode.build("field", DataForms.NS_URI)) { var="muc#role", label="Requested role", value="participant" };
|
||||
|
||||
submit_node.add_field(field_node);
|
||||
submit_node.add_field(single_field);
|
||||
|
||||
message.stanza.put_node(submit_node.stanza_node);
|
||||
|
||||
stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, message);
|
||||
}
|
||||
|
||||
public void kick(XmppStream stream, Jid jid, string nick) {
|
||||
change_role(stream, jid, nick, "none");
|
||||
}
|
||||
|
@ -530,6 +551,38 @@ public class ReceivedPipelineListener : StanzaListener<MessageStanza> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
StanzaNode? x_field_node = message.stanza.get_subnode("x", DataForms.NS_URI);
|
||||
if (x_field_node != null){
|
||||
Gee.List<StanzaNode>? fields = x_field_node.get_subnodes("field", DataForms.NS_URI);
|
||||
Jid? from_jid = null;
|
||||
string? nick = null;
|
||||
string? role = null;
|
||||
string? label = null;
|
||||
|
||||
if (fields.size!=0){
|
||||
foreach (var field_node in fields){
|
||||
string? var_ = field_node.get_attribute("var");
|
||||
if (var_ == "muc#jid"){
|
||||
StanzaNode? value_node = field_node.get_subnode("value", DataForms.NS_URI);
|
||||
if (value_node != null) from_jid = new Jid(value_node.get_string_content());
|
||||
}
|
||||
else if (var_ == "muc#roomnick"){
|
||||
StanzaNode? value_node = field_node.get_subnode("value", DataForms.NS_URI);
|
||||
if (value_node != null) nick = value_node.get_string_content();
|
||||
}
|
||||
else if (var_ == "muc#role"){
|
||||
StanzaNode? value_node = field_node.get_subnode("value", DataForms.NS_URI);
|
||||
if (value_node != null) role = value_node.get_string_content();
|
||||
}
|
||||
else if (var_ == "muc#request_allow"){
|
||||
label = field_node.get_attribute("label");
|
||||
}
|
||||
}
|
||||
outer.voice_request_received(stream, message.from, from_jid, nick, role, label);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue