aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdino/src/plugin/interfaces.vala2
-rw-r--r--main/CMakeLists.txt1
-rw-r--r--main/data/conversation_details.css30
-rw-r--r--main/data/conversation_details.ui45
-rw-r--r--main/data/gresource.xml1
-rw-r--r--main/data/muc_member_list_row.ui86
-rw-r--r--main/src/ui/application.vala14
-rw-r--r--main/src/ui/contact_details/permissions_provider.vala5
-rw-r--r--main/src/ui/contact_details/settings_provider.vala5
-rw-r--r--main/src/ui/conversation_details.vala26
-rw-r--r--main/src/ui/conversation_titlebar/menu_entry.vala3
-rw-r--r--main/src/ui/util/preference_group.vala154
-rw-r--r--main/src/view_model/conversation_details.vala83
-rw-r--r--main/src/windows/conversation_details.vala67
-rw-r--r--plugins/omemo/CMakeLists.txt1
-rw-r--r--plugins/omemo/data/encryption_preferences_entry.ui2
-rw-r--r--plugins/omemo/meson.build1
-rw-r--r--plugins/omemo/src/plugin.vala12
-rw-r--r--plugins/omemo/src/ui/bad_messages_populator.vala5
-rw-r--r--plugins/omemo/src/ui/contact_details_dialog.vala353
-rw-r--r--plugins/omemo/src/ui/contact_details_provider.vala38
-rw-r--r--plugins/omemo/src/ui/device_notification_populator.vala13
-rw-r--r--plugins/omemo/src/ui/encryption_preferences_entry.vala18
-rw-r--r--plugins/openpgp/src/contact_details_provider.vala49
-rw-r--r--plugins/openpgp/src/util.vala37
25 files changed, 483 insertions, 568 deletions
diff --git a/libdino/src/plugin/interfaces.vala b/libdino/src/plugin/interfaces.vala
index dd25c5f5..ea23b82a 100644
--- a/libdino/src/plugin/interfaces.vala
+++ b/libdino/src/plugin/interfaces.vala
@@ -64,8 +64,10 @@ public abstract class EncryptionPreferencesEntry : Object {
public interface ContactDetailsProvider : Object {
public abstract string id { get; }
+ public abstract string tab { get; }
public abstract void populate(Conversation conversation, ContactDetails contact_details, WidgetType type);
+ public abstract Object? get_widget(Conversation conversation);
}
public class ContactDetails : Object {
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 70665903..5cc1a8bf 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -89,6 +89,7 @@ set(RESOURCE_LIST
menu_conversation.ui
menu_encryption.ui
message_item_widget_edit_mode.ui
+ muc_member_list_row.ui
occupant_list.ui
occupant_list_item.ui
quote.ui
diff --git a/main/data/conversation_details.css b/main/data/conversation_details.css
index 0eaf60c0..f0cbf666 100644
--- a/main/data/conversation_details.css
+++ b/main/data/conversation_details.css
@@ -1,7 +1,31 @@
-.extended-headerbar {
- background-color: @headerbar_bg_color;
-}
.extended-headerbar-end {
padding-bottom: 24px;
border-bottom: 1px solid @borders;
+}
+
+.extended-headerbar {
+ background-color: @headerbar_bg_color;
+ padding-bottom: 0;
+}
+
+.dino-underlined-tabs .toggle {
+ background: none;
+ padding: 0;
+ margin: 0 12px 0 0;
+ border-left: none;
+ min-width: 0;
+}
+
+.dino-underlined-tabs .toggle label {
+ border-bottom: 2px solid transparent;
+ margin: 0;
+ padding: 6px;
+}
+
+.dino-underlined-tabs .toggle:checked label {
+ border-bottom-color: @accent_color;
+}
+
+.dino-underlined-tabs .toggle:hover:not(:checked) label {
+ border-bottom-color: alpha(@accent_color, 0.25);
} \ No newline at end of file
diff --git a/main/data/conversation_details.ui b/main/data/conversation_details.ui
index 5ee156bb..593dd4bf 100644
--- a/main/data/conversation_details.ui
+++ b/main/data/conversation_details.ui
@@ -134,25 +134,46 @@
</child>
</object>
</child>
+ <child>
+ <object class="GtkStackSwitcher">
+ <property name="stack">stack</property>
+ <property name="halign">start</property>
+ <property name="margin-start">12</property>
+ <property name="margin-end">12</property>
+ <style>
+ <class name="dino-underlined-tabs"/>
+ </style>
+ </object>
+ </child>
</object>
</child>
</object>
</child>
<child>
- <object class="GtkScrolledWindow">
- <property name="propagate-natural-height">True</property>
+ <object class="GtkStack" id="stack">
<child>
- <object class="AdwClamp">
- <child>
- <object class="GtkBox" id="about_box">
- <property name="orientation">vertical</property>
- <property name="spacing">12</property>
- <property name="margin-end">12</property>
- <property name="margin-start">12</property>
- <property name="margin-top">12</property>
- <property name="margin-bottom">40</property>
+ <object class="GtkStackPage">
+ <property name="title">About</property>
+ <property name="name">about</property>
+ <property name="child">
+ <object class="GtkScrolledWindow">
+ <property name="propagate-natural-height">True</property>
+ <child>
+ <object class="AdwClamp">
+ <child>
+ <object class="GtkBox" id="about_box">
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <property name="margin-end">12</property>
+ <property name="margin-start">12</property>
+ <property name="margin-top">18</property>
+ <property name="margin-bottom">40</property>
+ </object>
+ </child>
+ </object>
+ </child>
</object>
- </child>
+ </property>
</object>
</child>
</object>
diff --git a/main/data/gresource.xml b/main/data/gresource.xml
index 661185ab..88858e13 100644
--- a/main/data/gresource.xml
+++ b/main/data/gresource.xml
@@ -70,6 +70,7 @@
<file>menu_conversation.ui</file>
<file>menu_encryption.ui</file>
<file>message_item_widget_edit_mode.ui</file>
+ <file>muc_member_list_row.ui</file>
<file>occupant_list.ui</file>
<file>occupant_list_item.ui</file>
<file>quote.ui</file>
diff --git a/main/data/muc_member_list_row.ui b/main/data/muc_member_list_row.ui
new file mode 100644
index 00000000..cefad3fc
--- /dev/null
+++ b/main/data/muc_member_list_row.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="margin-top">10</property>
+ <property name="margin-bottom">10</property>
+ <property name="margin-start">7</property>
+ <property name="margin-end">14</property>
+ <child>
+ <object class="DinoUiAvatarPicture" id="picture">
+ <property name="height-request">35</property>
+ <property name="width-request">35</property>
+ <property name="valign">start</property>
+ <binding name="model">
+ <lookup name="avatar" type="DinoUiViewModelConferenceMemberListRow">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="margin-start">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <child>
+ <object class="GtkLabel" id="name_label">
+ <property name="max_width_chars">1</property>
+ <property name="ellipsize">end</property>
+ <property name="hexpand">True</property>
+ <property name="margin-end">7</property>
+ <property name="xalign">0</property>
+ <binding name="label">
+ <lookup name="name" type="DinoUiViewModelConferenceMemberListRow">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="affiliation_label">
+ <property name="margin-end">6</property>
+ <property name="xalign">1</property>
+ <binding name="label">
+ <lookup name="affiliation-str" type="DinoUiViewModelConferenceMemberListRow">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="spacing">4</property>
+ <property name="orientation">horizontal</property>
+ <child>
+ <object class="GtkLabel" id="jid_label">
+ <property name="ellipsize">end</property>
+ <property name="xalign">0</property>
+ <property name="margin-end">7</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <attributes>
+ <attribute name="scale" value="0.8"/>
+ </attributes>
+ <binding name="label">
+ <lookup name="jid" type="DinoUiViewModelConferenceMemberListRow">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface> \ No newline at end of file
diff --git a/main/src/ui/application.vala b/main/src/ui/application.vala
index 3c816a77..796146f8 100644
--- a/main/src/ui/application.vala
+++ b/main/src/ui/application.vala
@@ -145,6 +145,20 @@ public class Dino.Ui.Application : Adw.Application, Dino.Application {
});
add_action(open_conversation_action);
+ SimpleAction open_conversation_details_action = new SimpleAction("open-conversation-details", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.STRING}));
+ open_conversation_details_action.activate.connect((variant) => {
+ int conversation_id = variant.get_child_value(0).get_int32();
+ Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id);
+ if (conversation == null) return;
+
+ string stack_value = variant.get_child_value(1).get_string();
+
+ var conversation_details = ConversationDetails.setup_dialog(conversation, stream_interactor, window);
+ conversation_details.stack.visible_child_name = stack_value;
+ conversation_details.present();
+ });
+ add_action(open_conversation_details_action);
+
SimpleAction deny_subscription_action = new SimpleAction("deny-subscription", VariantType.INT32);
deny_subscription_action.activate.connect((variant) => {
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32());
diff --git a/main/src/ui/contact_details/permissions_provider.vala b/main/src/ui/contact_details/permissions_provider.vala
index c7ea4a1c..a0ca4d13 100644
--- a/main/src/ui/contact_details/permissions_provider.vala
+++ b/main/src/ui/contact_details/permissions_provider.vala
@@ -6,6 +6,7 @@ namespace Dino.Ui.ContactDetails {
public class PermissionsProvider : Plugins.ContactDetailsProvider, Object {
public string id { get { return "permissions"; } }
+ public string tab { get { return "about"; } }
private StreamInteractor stream_interactor;
@@ -25,6 +26,10 @@ public class PermissionsProvider : Plugins.ContactDetailsProvider, Object {
contact_details.add("Permissions", _("Request permission to send messages"), "", voice_request);
}
}
+
+ public Object? get_widget(Conversation conversation) {
+ return null;
+ }
}
}
diff --git a/main/src/ui/contact_details/settings_provider.vala b/main/src/ui/contact_details/settings_provider.vala
index 6a680f64..c6b87652 100644
--- a/main/src/ui/contact_details/settings_provider.vala
+++ b/main/src/ui/contact_details/settings_provider.vala
@@ -6,6 +6,7 @@ namespace Dino.Ui.ContactDetails {
public class SettingsProvider : Plugins.ContactDetailsProvider, Object {
public string id { get { return "chat_settings"; } }
+ public string tab { get { return "about"; } }
private StreamInteractor stream_interactor;
@@ -69,6 +70,10 @@ public class SettingsProvider : Plugins.ContactDetailsProvider, Object {
combobox.append("off", _("Off"));
return combobox;
}
+
+ public Object? get_widget(Conversation conversation) {
+ return null;
+ }
}
}
diff --git a/main/src/ui/conversation_details.vala b/main/src/ui/conversation_details.vala
index 4c6a0481..4b8a53b3 100644
--- a/main/src/ui/conversation_details.vala
+++ b/main/src/ui/conversation_details.vala
@@ -1,3 +1,4 @@
+using Dino;
using Dino.Entities;
using Xmpp;
using Xmpp.Xep;
@@ -24,6 +25,17 @@ namespace Dino.Ui.ConversationDetails {
public void bind_dialog(Model.ConversationDetails model, ViewModel.ConversationDetails view_model, StreamInteractor stream_interactor) {
view_model.avatar = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(model.conversation);
view_model.show_blocked = model.conversation.type_ == Conversation.Type.CHAT && stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(model.conversation.account);
+ view_model.members_sorted.set_model(model.members);
+ view_model.members.set_map_func((item) => {
+ var conference_member = (Ui.Model.ConferenceMember) item;
+ Jid? nick_jid = stream_interactor.get_module(MucManager.IDENTITY).get_occupant_jid(model.conversation.account, model.conversation.counterpart, conference_member.jid);
+ return new Ui.ViewModel.ConferenceMemberListRow() {
+ avatar = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(model.conversation, conference_member.jid),
+ name = nick_jid != null ? nick_jid.resourcepart : conference_member.jid.localpart,
+ jid = conference_member.jid.to_string(),
+ affiliation = conference_member.affiliation
+ };
+ });
if (model.domain_blocked) {
view_model.blocked = DOMAIN;
@@ -123,10 +135,11 @@ namespace Dino.Ui.ConversationDetails {
});
}
- public Window setup_dialog(Conversation conversation, StreamInteractor stream_interactor, Window parent) {
+ public Dialog setup_dialog(Conversation conversation, StreamInteractor stream_interactor, Window parent) {
var dialog = new Dialog() { transient_for = parent };
var model = new Model.ConversationDetails();
- populate_dialog(model, conversation, stream_interactor);
+ model.populate(stream_interactor, conversation);
+// populate_dialog(model, conversation, stream_interactor);
bind_dialog(model, dialog.model, stream_interactor);
dialog.model.about_rows.append(new ViewModel.PreferencesRow.Text() {
@@ -171,6 +184,10 @@ namespace Dino.Ui.ConversationDetails {
app.plugin_registry.register_contact_details_entry(new ContactDetails.PermissionsProvider(stream_interactor));
foreach (Plugins.ContactDetailsProvider provider in app.plugin_registry.contact_details_entries) {
+ var preferences_group = (Adw.PreferencesGroup) provider.get_widget(conversation);
+ if (preferences_group != null) {
+ dialog.add_encryption_tab_element((Adw.PreferencesGroup) provider.get_widget(conversation));
+ }
provider.populate(conversation, contact_details, Plugins.WidgetType.GTK4);
}
@@ -195,14 +212,13 @@ namespace Dino.Ui.ConversationDetails {
};
switch (category) {
- case "Encryption":
- dialog.model.encryption_rows.append(view_model);
- break;
case "Permissions":
case "Local Settings":
case "Settings":
dialog.model.settings_rows.append(view_model);
break;
+ default:
+ break;
}
}
}
diff --git a/main/src/ui/conversation_titlebar/menu_entry.vala b/main/src/ui/conversation_titlebar/menu_entry.vala
index 8a3d525f..7a6688ae 100644
--- a/main/src/ui/conversation_titlebar/menu_entry.vala
+++ b/main/src/ui/conversation_titlebar/menu_entry.vala
@@ -25,7 +25,8 @@ class MenuEntry : Plugins.ConversationTitlebarEntry, Object {
SimpleActionGroup action_group = new SimpleActionGroup();
SimpleAction details_action = new SimpleAction("details", null);
details_action.activate.connect((parameter) => {
- open_conversation_details();
+ var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.string("about")});
+ GLib.Application.get_default().activate_action("open-conversation-details", variant);
});
action_group.insert(details_action);
SimpleAction close_action = new SimpleAction("close", null);
diff --git a/main/src/ui/util/preference_group.vala b/main/src/ui/util/preference_group.vala
index 8f3a34f3..fca3d677 100644
--- a/main/src/ui/util/preference_group.vala
+++ b/main/src/ui/util/preference_group.vala
@@ -11,96 +11,98 @@ namespace Dino.Ui.Util {
for (int preference_group_i = 0; preference_group_i < row_view_models.get_n_items(); preference_group_i++) {
var preferences_row = (ViewModel.PreferencesRow.Any) row_view_models.get_item(preference_group_i);
- Widget? w = null;
+ Widget? w = row_to_preference_row(preferences_row);
+ if (w == null) continue;
- var entry_view_model = preferences_row as ViewModel.PreferencesRow.Entry;
- if (entry_view_model != null) {
- Adw.EntryRow view = new Adw.EntryRow() { title = entry_view_model.title, show_apply_button=true };
- entry_view_model.bind_property("text", view, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from, ref to) => {
- var str = (string) from;
- to = str ?? "";
- return true;
- });
- view.apply.connect(() => {
- entry_view_model.changed();
- });
- w = view;
- }
+ preference_group.add(w);
+ }
- var password_view_model = preferences_row as ViewModel.PreferencesRow.PrivateText;
- if (password_view_model != null) {
- Adw.PasswordEntryRow view = new Adw.PasswordEntryRow() { title = password_view_model.title, show_apply_button=true };
- password_view_model.bind_property("text", view, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from, ref to) => {
- var str = (string) from;
- to = str ?? "";
- return true;
- });
- view.apply.connect(() => {
- password_view_model.changed();
- });
+ return preference_group;
+ }
- w = view;
- }
+ public Adw.PreferencesRow? row_to_preference_row(ViewModel.PreferencesRow.Any preferences_row) {
+ var entry_view_model = preferences_row as ViewModel.PreferencesRow.Entry;
+ if (entry_view_model != null) {
+ Adw.EntryRow view = new Adw.EntryRow() { title = entry_view_model.title, show_apply_button=true };
+ entry_view_model.bind_property("text", view, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from, ref to) => {
+ var str = (string) from;
+ to = str ?? "";
+ return true;
+ });
+ view.apply.connect(() => {
+ entry_view_model.changed();
+ });
+ return view;
+ }
+
+ var password_view_model = preferences_row as ViewModel.PreferencesRow.PrivateText;
+ if (password_view_model != null) {
+ Adw.PasswordEntryRow view = new Adw.PasswordEntryRow() { title = password_view_model.title, show_apply_button=true };
+ password_view_model.bind_property("text", view, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from, ref to) => {
+ var str = (string) from;
+ to = str ?? "";
+ return true;
+ });
+ view.apply.connect(() => {
+ password_view_model.changed();
+ });
+
+ return view;
+ }
- var row_text = preferences_row as ViewModel.PreferencesRow.Text;
- if (row_text != null) {
- w = new Adw.ActionRow() {
- title = row_text.title,
- subtitle = row_text.text,
+ var row_text = preferences_row as ViewModel.PreferencesRow.Text;
+ if (row_text != null) {
+ var view = new Adw.ActionRow() {
+ title = row_text.title,
+ subtitle = row_text.text,
#if Adw_1_3
- subtitle_selectable = true,
+ subtitle_selectable = true,
#endif
- };
- w.add_css_class("property");
+ };
+ view.add_css_class("property");
- Util.force_css(w, "row.property > box.header > box.title > .title { font-weight: 400; font-size: 9pt; opacity: 0.55; }");
- Util.force_css(w, "row.property > box.header > box.title > .subtitle { font-size: inherit; opacity: 1; }");
- }
+ Util.force_css(view, "row.property > box.header > box.title > .title { font-weight: 400; font-size: 9pt; opacity: 0.55; }");
+ Util.force_css(view, "row.property > box.header > box.title > .subtitle { font-size: inherit; opacity: 1; }");
+ return view;
+ }
- var toggle_view_model = preferences_row as ViewModel.PreferencesRow.Toggle;
- if (toggle_view_model != null) {
- var view = new Adw.ActionRow() { title = toggle_view_model.title, subtitle = toggle_view_model.subtitle };
- var toggle = new Switch() { valign = Align.CENTER };
- view.activatable_widget = toggle;
- view.add_suffix(toggle);
- toggle_view_model.bind_property("state", toggle, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
- w = view;
- }
+ var toggle_view_model = preferences_row as ViewModel.PreferencesRow.Toggle;
+ if (toggle_view_model != null) {
+ var view = new Adw.ActionRow() { title = toggle_view_model.title, subtitle = toggle_view_model.subtitle };
+ var toggle = new Switch() { valign = Align.CENTER };
+ view.activatable_widget = toggle;
+ view.add_suffix(toggle);
+ toggle_view_model.bind_property("state", toggle, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+ return view;
+ }
- var combobox_view_model = preferences_row as ViewModel.PreferencesRow.ComboBox;
- if (combobox_view_model != null) {
- var string_list = new StringList(null);
- foreach (string text in combobox_view_model.items) {
- string_list.append(text);
- }
+ var combobox_view_model = preferences_row as ViewModel.PreferencesRow.ComboBox;
+ if (combobox_view_model != null) {
+ var string_list = new StringList(null);
+ foreach (string text in combobox_view_model.items) {
+ string_list.append(text);
+ }
#if Adw_1_4
- var view = new Adw.ComboRow() { title = combobox_view_model.title };
- view.model = string_list;
- combobox_view_model.bind_property("active-item", view, "selected", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+ var view = new Adw.ComboRow() { title = combobox_view_model.title };
+ view.model = string_list;
+ combobox_view_model.bind_property("active-item", view, "selected", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
#else
- var view = new Adw.ActionRow() { title = combobox_view_model.title };
- var drop_down = new DropDown(string_list, null) { valign = Align.CENTER };
- combobox_view_model.bind_property("active-item", drop_down, "selected", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
- view.activatable_widget = drop_down;
- view.add_suffix(drop_down);
+ var view = new Adw.ActionRow() { title = combobox_view_model.title };
+ var drop_down = new DropDown(string_list, null) { valign = Align.CENTER };
+ combobox_view_model.bind_property("active-item", drop_down, "selected", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL);
+ view.activatable_widget = drop_down;
+ view.add_suffix(drop_down);
#endif
- w = view;
- }
-
- var widget_view_model = preferences_row as ViewModel.PreferencesRow.WidgetDeprecated;
- if (widget_view_model != null) {
- var view = new Adw.ActionRow() { title = widget_view_model.title };
- view.add_suffix(widget_view_model.widget);
- w = view;
- }
-
- if (w == null) {
- continue;
- }
+ return view;
+ }
- preference_group.add(w);
+ var widget_view_model = preferences_row as ViewModel.PreferencesRow.WidgetDeprecated;
+ if (widget_view_model != null) {
+ var view = new Adw.ActionRow() { title = widget_view_model.title };
+ view.add_suffix(widget_view_model.widget);
+ return view;
}
- return preference_group;
+ return null;
}
} \ No newline at end of file
diff --git a/main/src/view_model/conversation_details.vala b/main/src/view_model/conversation_details.vala
index 75fc9669..3641d584 100644
--- a/main/src/view_model/conversation_details.vala
+++ b/main/src/view_model/conversation_details.vala
@@ -39,13 +39,42 @@ public class Dino.Ui.ViewModel.ConversationDetails : Object {
public bool show_blocked { get; set; }
public BlockState blocked { get; set; }
- public GLib.ListStore preferences_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
public GLib.ListStore about_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
- public GLib.ListStore encryption_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
public GLib.ListStore settings_rows = new GLib.ListStore(typeof(PreferencesRow.Any));
public GLib.ListStore room_configuration_rows { get; set; }
+ public MapListModel members = new MapListModel(null, null);
+ public SortListModel members_sorted = new SortListModel(null, new MucMemberSorter());
+
+ construct {
+ members = new MapListModel(members_sorted, null);
+ }
}
+public class MucMemberSorter : Sorter {
+
+ public override Gtk.Ordering compare(GLib.Object? item1, GLib.Object? item2) {
+ var member_list_row1 = (Dino.Ui.Model.ConferenceMember) item1;
+ var member_list_row2 = (Dino.Ui.Model.ConferenceMember) item2;
+ var test = new Xmpp.Xep.Muc.Affiliation[] { OWNER, ADMIN, MEMBER };
+ var affiliation_ordering = new ArrayList<Xmpp.Xep.Muc.Affiliation>.wrap(test);
+
+ var affiliation_sorting = affiliation_ordering.index_of(member_list_row1.affiliation) - affiliation_ordering.index_of(member_list_row2.affiliation);
+ if (affiliation_sorting == 0) {
+ return Ordering.from_cmpfunc(member_list_row1.name.collate(member_list_row2.name));
+ }
+
+ return Ordering.from_cmpfunc(affiliation_sorting);
+ }
+
+ public override Gtk.SorterOrder get_order() {
+ return SorterOrder.TOTAL;
+ }
+}
+
+//public class Dino.Ui.ViewModel.ConferenceDetails : Dino.Ui.ViewModel.ConversationDetails {
+// public static
+//}
+
public class Dino.Ui.Model.ConversationDetails : Object {
public Conversation conversation { get; set; }
public Dino.Model.ConversationDisplayName display_name { get; set; }
@@ -53,4 +82,54 @@ public class Dino.Ui.Model.ConversationDetails : Object {
public string? data_form_bak;
public bool blocked { get; set; }
public bool domain_blocked { get; set; }
+
+ public GLib.ListStore members = new GLib.ListStore(typeof(Ui.Model.ConferenceMember));
+
+ public void populate(StreamInteractor stream_interactor, Conversation conversation) {
+ Ui.ConversationDetails.populate_dialog(this, conversation, stream_interactor);
+
+ if (conversation.type_ == GROUPCHAT) {
+ Gee.List<Jid>? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account);
+ if (occupants != null) {
+ foreach (Jid occupant in occupants) {
+ var affiliation = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, occupant, conversation.account);
+ members.append(new Dino.Ui.Model.ConferenceMember() {
+ name = occupant.to_string(),
+ jid = occupant,
+ affiliation = affiliation
+ });
+ }
+ }
+ }
+ }
+}
+
+public class Dino.Ui.Model.ConferenceMember : Object {
+ public string name { get; set; }
+ public Jid jid { get; set; }
+ public Xmpp.Xep.Muc.Affiliation affiliation { get; set; }
}
+
+public class Dino.Ui.ViewModel.ConferenceMemberListRow : Object {
+ public ViewModel.CompatAvatarPictureModel avatar { get; set; }
+ public string name { get; set; }
+ public string jid { get; set; }
+ public Xmpp.Xep.Muc.Affiliation affiliation { get; set; }
+ public string? affiliation_str { get; set; }
+
+ construct {
+ this.bind_property("affiliation", this, "affiliation-str", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from_value, ref to_value) => {
+ to_value = affiliation_to_str((Xmpp.Xep.Muc.Affiliation) from_value);
+ return true;
+ });
+ }
+
+ private string? affiliation_to_str(Xmpp.Xep.Muc.Affiliation affiliation) {
+ switch (affiliation) {
+ case OWNER: return _("Owner");
+ case ADMIN: return _("Admin");
+ case MEMBER: return _("Member");
+ default: return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/main/src/windows/conversation_details.vala b/main/src/windows/conversation_details.vala
index c2484db8..5c0c78b3 100644
--- a/main/src/windows/conversation_details.vala
+++ b/main/src/windows/conversation_details.vala
@@ -8,6 +8,7 @@ namespace Dino.Ui.ConversationDetails {
[GtkTemplate (ui = "/im/dino/Dino/conversation_details.ui")]
public class Dialog : Adw.Window {
+ [GtkChild] public unowned Stack stack;
[GtkChild] public unowned Box about_box;
[GtkChild] public unowned Button pin_button;
[GtkChild] public unowned Adw.ButtonContent pin_button_content;
@@ -22,6 +23,12 @@ namespace Dino.Ui.ConversationDetails {
[GtkChild] public unowned ViewModel.ConversationDetails model { get; }
+ public StackPage? encryption_stack_page = null;
+ public Box? encryption_box = null;
+
+ public StackPage? member_stack_page = null;
+ public Box? member_box = null;
+
private SimpleAction block_action = new SimpleAction.stateful("block", VariantType.INT32, new Variant.int32(ViewModel.ConversationDetails.BlockState.UNBLOCK));
class construct {
@@ -44,10 +51,12 @@ namespace Dino.Ui.ConversationDetails {
model.notify["notification-is-default"].connect(update_notification_button_visibility);
model.about_rows.items_changed.connect(create_preferences_rows);
- model.encryption_rows.items_changed.connect(create_preferences_rows);
model.settings_rows.items_changed.connect(create_preferences_rows);
model.notify["room-configuration-rows"].connect(create_preferences_rows);
+ model.notify["members"].connect(create_members);
+ create_members();
+
// Create block action
SimpleActionGroup block_action_group = new SimpleActionGroup();
block_action = new SimpleAction.stateful("block", VariantType.INT32, new Variant.int32(0));
@@ -145,6 +154,30 @@ namespace Dino.Ui.ConversationDetails {
}
}
+ private void create_members() {
+ if (model.members_sorted.n_items == 0) return;
+
+ var selection_model = new NoSelection(model.members_sorted);
+ var item_factory = new BuilderListItemFactory.from_resource(null, "/im/dino/Dino/muc_member_list_row.ui");
+ var list_view = new ListView(selection_model, item_factory) { single_click_activate = true };
+ list_view.add_css_class("card");
+ list_view.activate.connect((position) => {
+// var widget = (Gtk.Widget) list_view.observe_children().get_item(position);
+// var name_label = widget.get_template_child(Type.OBJECT, "name-label");
+// print(widget.get_type().name());
+
+// var popover = new Popover();
+// popover.parent = widget;
+// popover.popup();
+
+
+ var row_view_model = (Ui.Model.ConferenceMember) model.members_sorted.get_item(position);
+ print(@"$(position) $(row_view_model.name)\n");
+ });
+
+ add_members_tab_element(list_view);
+ }
+
private void create_preferences_rows() {
var widget = about_box.get_first_child();
while (widget != null) {
@@ -155,9 +188,6 @@ namespace Dino.Ui.ConversationDetails {
if (model.about_rows.get_n_items() > 0) {
about_box.append(Util.rows_to_preference_group(model.about_rows, _("About")));
}
- if (model.encryption_rows.get_n_items() > 0) {
- about_box.append(Util.rows_to_preference_group(model.encryption_rows, _("Encryption")));
- }
if (model.settings_rows.get_n_items() > 0) {
about_box.append(Util.rows_to_preference_group(model.settings_rows, _("Settings")));
}
@@ -165,5 +195,34 @@ namespace Dino.Ui.ConversationDetails {
about_box.append(Util.rows_to_preference_group(model.room_configuration_rows, _("Room Configuration")));
}
}
+
+ public void add_encryption_tab_element(Adw.PreferencesGroup preferences_group) {
+ if (encryption_stack_page == null) {
+ encryption_box = new Box(Orientation.VERTICAL, 12) { margin_end = 12, margin_start = 12, margin_top = 18, margin_bottom = 40 };
+ var scrolled_window = new ScrolledWindow() { vexpand = true };
+ var clamp = new Adw.Clamp();
+ clamp.set_child(encryption_box);
+ scrolled_window.set_child(clamp);
+ encryption_stack_page = stack.add_child(scrolled_window);
+ encryption_stack_page.title = _("Encryption");
+ encryption_stack_page.name = "encryption";
+ }
+ encryption_box.append(preferences_group);
+ }
+
+ public void add_members_tab_element(Widget widget) {
+ if (member_stack_page == null) {
+ member_box = new Box(Orientation.VERTICAL, 12) { margin_end = 12, margin_start = 12, margin_top = 18 };
+ member_stack_page = stack.add_child(member_box);
+ member_stack_page.title = _("Members");
+ member_stack_page.name = "member";
+ }
+ member_box.append(widget);
+ }
+ }
+
+ [GtkTemplate (ui = "/im/dino/Dino/muc_member_list_row.ui")]
+ public class Dino.Ui.ConversationDetails.MemberListItem : Gtk.Widget {
+
}
}
diff --git a/plugins/omemo/CMakeLists.txt b/plugins/omemo/CMakeLists.txt
index 410d5712..00130863 100644
--- a/plugins/omemo/CMakeLists.txt
+++ b/plugins/omemo/CMakeLists.txt
@@ -72,7 +72,6 @@ SOURCES
src/ui/bad_messages_populator.vala
src/ui/call_encryption_entry.vala
src/ui/contact_details_provider.vala
- src/ui/contact_details_dialog.vala
src/ui/device_notification_populator.vala
src/ui/own_notifications.vala
src/ui/encryption_list_entry.vala
diff --git a/plugins/omemo/data/encryption_preferences_entry.ui b/plugins/omemo/data/encryption_preferences_entry.ui
index 7ca26224..e84f56b4 100644
--- a/plugins/omemo/data/encryption_preferences_entry.ui
+++ b/plugins/omemo/data/encryption_preferences_entry.ui
@@ -21,7 +21,9 @@
</object>
</child>
<child>
+ <!-- TODO: make this a SwitchRow once we depend on Adwaita 1.4-->
<object class="AdwActionRow" id="automatically_accept_new_row">
+ <property name="activatable-widget">automatically_accept_new_switch</property>
<child type="suffix">
<object class="GtkSwitch" id="automatically_accept_new_switch">
<property name="valign">center</property>
diff --git a/plugins/omemo/meson.build b/plugins/omemo/meson.build
index 05d7c265..f1fdb46d 100644
--- a/plugins/omemo/meson.build
+++ b/plugins/omemo/meson.build
@@ -43,7 +43,6 @@ sources = files(
'src/trust_level.vala',
'src/ui/bad_messages_populator.vala',
'src/ui/call_encryption_entry.vala',
- 'src/ui/contact_details_dialog.vala',
'src/ui/contact_details_provider.vala',
'src/ui/device_notification_populator.vala',
'src/ui/encryption_list_entry.vala',
diff --git a/plugins/omemo/src/plugin.vala b/plugins/omemo/src/plugin.vala
index dfbe0780..b6142ba6 100644
--- a/plugins/omemo/src/plugin.vala
+++ b/plugins/omemo/src/plugin.vala
@@ -72,18 +72,6 @@ public class Plugin : RootInterface, Object {
Manager.start(this.app.stream_interactor, db, trust_manager, encryptors);
- SimpleAction own_keys_action = new SimpleAction("own-keys", VariantType.INT32);
- own_keys_action.activate.connect((variant) => {
- foreach(Dino.Entities.Account account in this.app.stream_interactor.get_accounts()) {
- if(account.id == variant.get_int32()) {
- ContactDetailsDialog dialog = new ContactDetailsDialog(this, account, account.bare_jid);
- dialog.set_transient_for(((Gtk.Application) this.app).get_active_window());
- dialog.present();
- }
- }
- });
- this.app.add_action(own_keys_action);
-
string locales_dir;
if (app.search_path_generator != null) {
locales_dir = ((!)app.search_path_generator).get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR);
diff --git a/plugins/omemo/src/ui/bad_messages_populator.vala b/plugins/omemo/src/ui/bad_messages_populator.vala
index 8f087482..d87108ea 100644
--- a/plugins/omemo/src/ui/bad_messages_populator.vala
+++ b/plugins/omemo/src/ui/bad_messages_populator.vala
@@ -180,9 +180,8 @@ public class BadMessagesWidget : Box {
}
private bool on_label_activate_link() {
- ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, conversation.account, jid);
- dialog.set_transient_for((Window) get_root());
- dialog.present();
+ var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.string("encryption")});
+ GLib.Application.get_default().activate_action("open-conversation-details", variant);
return false;
}
diff --git a/plugins/omemo/src/ui/contact_details_dialog.vala b/plugins/omemo/src/ui/contact_details_dialog.vala
deleted file mode 100644
index 1fddd759..00000000
--- a/plugins/omemo/src/ui/contact_details_dialog.vala
+++ /dev/null
@@ -1,353 +0,0 @@
-using Gtk;
-using Xmpp;
-using Gee;
-using Qlite;
-using Dino.Entities;
-using Qrencode;
-using Gdk;
-
-namespace Dino.Plugins.Omemo {
-
-[GtkTemplate (ui = "/im/dino/Dino/omemo/contact_details_dialog.ui")]
-public class ContactDetailsDialog : Gtk.Dialog {
-
- private Plugin plugin;
- private Account account;
- private Jid jid;
- private bool own = false;
- private int own_id = 0;
- private int identity_id = 0;
- private Signal.Store store;
- private Set<uint32> displayed_ids = new HashSet<uint32>();
-
- [GtkChild] private unowned Label automatically_accept_new_label;
- [GtkChild] private unowned Label automatically_accept_new_descr;
- [GtkChild] private unowned Label own_key_label;
- [GtkChild] private unowned Label new_keys_label;
- [GtkChild] private unowned Label associated_keys_label;
- [GtkChild] private unowned Label inactive_expander_label;
-
- [GtkChild] private unowned Box own_fingerprint_container;
- [GtkChild] private unowned Label own_fingerprint_label;
- [GtkChild] private unowned Box new_keys_container;
- [GtkChild] private unowned ListBox new_keys_listbox;
- [GtkChild] private unowned Box keys_container;
- [GtkChild] private unowned ListBox keys_listbox;
- [GtkChild] private unowned Expander inactive_keys_expander;
- [GtkChild] private unowned ListBox inactive_keys_listbox;
- [GtkChild] private unowned Switch auto_accept_switch;
- [GtkChild] private unowned Button copy_button;
- [GtkChild] private unowned MenuButton show_qrcode_button;
- [GtkChild] private unowned Picture qrcode_picture;
- [GtkChild] private unowned Popover qrcode_popover;
-
- private ArrayList<Widget> new_keys_listbox_children = new ArrayList<Widget>();
-
- construct {
- // If we set the strings in the .ui file, they don't get translated
- title = _("OMEMO Key Management");
- automatically_accept_new_label.label = _("Automatically accept new keys");
- automatically_accept_new_descr.label = _("New encryption keys from this contact will be accepted automatically.");
- own_key_label.label = _("Own key");
- new_keys_label.label = _("New keys");
- associated_keys_label.label = _("Associated keys");
- inactive_expander_label.label = _("Inactive keys");
- }
-
- public ContactDetailsDialog(Plugin plugin, Account account, Jid jid) {
- Object(use_header_bar : 1);
- this.plugin = plugin;
- this.account = account;
- this.jid = jid;
-
- keys_listbox.row_activated.connect(on_key_entry_clicked);
- inactive_keys_listbox.row_activated.connect(on_key_entry_clicked);
- auto_accept_switch.state_set.connect(on_auto_accept_toggled);
-
- identity_id = plugin.db.identity.get_id(account.id);
- if (identity_id < 0) return;
- Dino.Application? app = Application.get_default() as Dino.Application;
- if (app != null) {
- store = app.stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).store;
- }
-
- auto_accept_switch.set_active(plugin.db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string(), true));
-
- // Dialog opened from the account settings menu
- // Show the fingerprint for this device separately with buttons for a qrcode and to copy
- if(jid.equals(account.bare_jid)) {
- own = true;
- own_id = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id];
-
- automatically_accept_new_descr.label = _("New encryption keys from your other devices will be accepted automatically.");
-
- own_fingerprint_container.visible = true;
-
- string own_b64 = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.identity_key_public_base64];
- string fingerprint = fingerprint_from_base64(own_b64);
- own_fingerprint_label.set_markup(fingerprint_markup(fingerprint));
-
- copy_button.clicked.connect(() => { copy_button.get_clipboard().set_text(fingerprint); });
-
- int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id];
- var iri_query = @"omemo-sid-$(sid)=$(fingerprint)";
-#if GLIB_2_66 && VALA_0_50
- string iri = GLib.Uri.join(UriFlags.NONE, "xmpp", null, null, 0, jid.to_string(), iri_query, null);
-#else
- var iri_path_seg = escape_for_iri_path_segment(jid.to_string());
- var iri = @"xmpp:$(iri_path_seg)?$(iri_query)";
-#endif
-
- const int QUIET_ZONE_MODULES = 4; // MUST be at least 4
- const int MODULE_SIZE_PX = 4; // arbitrary
- var qr_paintable = new QRcode(iri, 2)
- .to_paintable(MODULE_SIZE_PX * qrcode_picture.scale_factor);
- qrcode_picture.paintable = qr_paintable;
- qrcode_picture.margin_top = qrcode_picture.margin_end =
- qrcode_picture.margin_bottom = qrcode_picture.margin_start = QUIET_ZONE_MODULES * MODULE_SIZE_PX;
- qrcode_popover.add_css_class("qrcode-container");
-
- show_qrcode_button.popover = qrcode_popover;
- }
-
- new_keys_listbox.set_header_func(header_function);
-
- keys_listbox.set_header_func(header_function);
-
- //Show any new devices for which the user must decide whether to accept or reject
- foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) {
- add_new_fingerprint(device);
- }
-
- //Show the normal devicelist
- foreach (Row device in plugin.db.identity_meta.get_known_devices(identity_id, jid.to_string())) {
- if(own && device[plugin.db.identity_meta.device_id] == own_id) {
- continue;
- }
- add_fingerprint(device, (TrustLevel) device[plugin.db.identity_meta.trust_level]);
- }
-
- // Check for unknown devices
- fetch_unknown_bundles();
- }
-
- private static string escape_for_iri_path_segment(string s) {
- // from RFC 3986, 2.2. Reserved Characters:
- string SUB_DELIMS = "!$&'()*+,;=";
- // from RFC 3986, 3.3. Path (pchar without unreserved and pct-encoded):
- string ALLOWED_RESERVED_CHARS = SUB_DELIMS + ":@";
- return GLib.Uri.escape_string(s, ALLOWED_RESERVED_CHARS, true);
- }
-
- private void fetch_unknown_bundles() {
- Dino.Application app = Application.get_default() as Dino.Application;
- XmppStream? stream = app.stream_interactor.get_stream(account);
- if (stream == null) return;
- StreamModule? module = stream.get_module(StreamModule.IDENTITY);
- if (module == null) return;
- module.bundle_fetched.connect_after((bundle_jid, device_id, bundle) => {
- if (bundle_jid.equals(jid) && !displayed_ids.contains(device_id)) {
- Row? device = plugin.db.identity_meta.get_device(identity_id, jid.to_string(), device_id);
- if (device == null) return;
- if (auto_accept_switch.active) {
- add_fingerprint(device, (TrustLevel) device[plugin.db.identity_meta.trust_level]);
- } else {
- add_new_fingerprint(device);
- }
- }
- });
- foreach (Row device in plugin.db.identity_meta.get_unknown_devices(identity_id, jid.to_string())) {
- try {
- module.fetch_bundle(stream, new Jid(device[plugin.db.identity_meta.address_name]), device[plugin.db.identity_meta.device_id], false);
- } catch (InvalidJidError e) {
- warning("Ignoring device with invalid Jid: %s", e.message);
- }
- }
- }
-
- private void header_function(ListBoxRow row, ListBoxRow? before) {
- if (row.get_header() == null && before != null) {
- row.set_header(new Separator(Orientation.HORIZONTAL));
- }
- }
-
- private void add_fingerprint(Row device, TrustLevel trust) {
- string key_base64 = device[plugin.db.identity_meta.identity_key_public_base64];
- bool key_active = device[plugin.db.identity_meta.now_active];
- if (store != null) {
- try {
- Signal.Address address = new Signal.Address(jid.to_string(), device[plugin.db.identity_meta.device_id]);
- Signal.SessionRecord? session = null;
- if (store.contains_session(address)) {
- session = store.load_session(address);
- string session_key_base64 = Base64.encode(session.state.remote_identity_key.serialize());
- if (key_base64 != session_key_base64) {
- critical("Session and database identity key mismatch!");
- key_base64 = session_key_base64;
- }
- }
- } catch (Error e) {
- print("Error while reading session store: %s", e.message);
- }
- }
- FingerprintRow fingerprint_row = new FingerprintRow(device, key_base64, trust, key_active) { visible = true, activatable = true, hexpand = true };
-
- if (device[plugin.db.identity_meta.now_active]) {
- keys_container.visible = true;
- keys_listbox.append(fingerprint_row);
- } else {
- inactive_keys_expander.visible=true;
- inactive_keys_listbox.append(fingerprint_row);
- }
- displayed_ids.add(device[plugin.db.identity_meta.device_id]);
- }
-
- private void on_key_entry_clicked(ListBoxRow widget) {
- FingerprintRow? fingerprint_row = widget as FingerprintRow;
- if (fingerprint_row == null) return;
-
- Row updated_device = plugin.db.identity_meta.get_device(fingerprint_row.row[plugin.db.identity_meta.identity_id], fingerprint_row.row[plugin.db.identity_meta.address_name], fingerprint_row.row[plugin.db.identity_meta.device_id]);
- ManageKeyDialog manage_dialog = new ManageKeyDialog(updated_device, plugin.db);
- manage_dialog.set_transient_for((Gtk.Window) get_root());
- manage_dialog.present();
- manage_dialog.response.connect((response) => {
- fingerprint_row.update_trust_state(response, fingerprint_row.row[plugin.db.identity_meta.now_active]);
- update_stored_trust(response, fingerprint_row.row);
- });
- }
-
- private bool on_auto_accept_toggled(bool active) {
- plugin.trust_manager.set_blind_trust(account, jid, active);
-
- if (active) {
- int identity_id = plugin.db.identity.get_id(account.id);
- if (identity_id < 0) return false;
-
- new_keys_container.visible = false;
- foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) {
- plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED);
- add_fingerprint(device, TrustLevel.TRUSTED);
- }
- }
- return false;
- }
-
- private void update_stored_trust(int response, Row device) {
- switch (response) {
- case TrustLevel.TRUSTED:
- plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED);
- break;
- case TrustLevel.UNTRUSTED:
- plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED);
- break;
- case TrustLevel.VERIFIED:
- plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.VERIFIED);
- plugin.trust_manager.set_blind_trust(account, jid, false);
- auto_accept_switch.set_active(false);
- break;
- }
- }
-
- private void add_new_fingerprint(Row device) {
- new_keys_container.visible = true;
-
- ListBoxRow lbr = new ListBoxRow() { visible = true, activatable = false, hexpand = true };
- Box box = new Box(Gtk.Orientation.HORIZONTAL, 40) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14, hexpand = true };
-
- Button accept_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true };
- accept_button.set_icon_name("emblem-ok-symbolic"); // using .image = sets .image-button. Together with .suggested/destructive action that breaks the button Adwaita
- accept_button.add_css_class("suggested-action");
- accept_button.tooltip_text = _("Accept key");
-
- Button reject_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true };
- reject_button.set_icon_name("action-unavailable-symbolic");
- reject_button.add_css_class("destructive-action");
- reject_button.tooltip_text = _("Reject key");
-
- accept_button.clicked.connect(() => {
- plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED);
- add_fingerprint(device, TrustLevel.TRUSTED);
- new_keys_listbox.remove(lbr);
- new_keys_listbox_children.remove(lbr);
- if (new_keys_listbox_children.size < 1) new_keys_container.visible = false;
- });
-
- reject_button.clicked.connect(() => {
- plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED);
- add_fingerprint(device, TrustLevel.UNTRUSTED);
- new_keys_listbox.remove(lbr);
- new_keys_listbox_children.remove(lbr);
- if (new_keys_listbox_children.size < 1) new_keys_container.visible = false;
- });
-
- string res = fingerprint_markup(fingerprint_from_base64(device[plugin.db.identity_meta.identity_key_public_base64]));
- Label fingerprint_label = new Label(res) { use_markup=true, justify=Justification.RIGHT, halign = Align.START, valign = Align.CENTER, hexpand = false };
- box.append(fingerprint_label);
-
- Box control_box = new Box(Gtk.Orientation.HORIZONTAL, 0) { visible = true, hexpand = true };
- control_box.append(accept_button);
- control_box.append(reject_button);
- control_box.add_css_class("linked"); // .linked: Visually link the accept / reject buttons
- box.append(control_box);
-
- lbr.set_child(box);
- new_keys_listbox.append(lbr);
- new_keys_listbox_children.add(lbr);
- displayed_ids.add(device[plugin.db.identity_meta.device_id]);
- }
-}
-
-public class FingerprintRow : ListBoxRow {
-
- private Image trust_image = new Image() { visible = true, halign = Align.END };
- private Label fingerprint_label = new Label("") { use_markup=true, justify=Justification.RIGHT, halign = Align.START, valign = Align.CENTER, hexpand = false };
- private Label trust_label = new Label(null) { visible = true, hexpand = true, xalign = 0 };
-
- public Row row;
-
- construct {
- Box box = new Box(Gtk.Orientation.HORIZONTAL, 40) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14, hexpand = true };
- Box status_box = new Box(Gtk.Orientation.HORIZONTAL, 5) { visible = true, hexpand = true };
-
- box.append(fingerprint_label);
- box.append(status_box);
-
- status_box.append(trust_label);
- status_box.append(trust_image);
-
- this.set_child(box);
- }
-
- public FingerprintRow(Row row, string key_base64, int trust, bool now_active) {
- this.row = row;
- fingerprint_label.label = fingerprint_markup(fingerprint_from_base64(key_base64));
- update_trust_state(trust, now_active);
- }
-
- public void update_trust_state(int trust, bool now_active) {
- switch(trust) {
- case TrustLevel.TRUSTED:
- trust_image.icon_name = "emblem-ok-symbolic";
- trust_label.set_markup("<span color='#1A63D9'>%s</span>".printf(_("Accepted")));
- fingerprint_label.remove_css_class("dim-label");
- break;
- case TrustLevel.UNTRUSTED:
- trust_image.icon_name = "action-unavailable-symbolic";
- trust_label.set_markup("<span color='#D91900'>%s</span>".printf(_("Rejected")));
- fingerprint_label.add_css_class("dim-label");
- break;
- case TrustLevel.VERIFIED:
- trust_image.icon_name = "security-high-symbolic";
- trust_label.set_markup("<span color='#1A63D9'>%s</span>".printf(_("Verified")));
- fingerprint_label.remove_css_class("dim-label");
- break;
- }
-
- if (!now_active) {
- trust_image.icon_name = "appointment-missed-symbolic";
- trust_label.set_markup("<span color='#8b8e8f'>%s</span>".printf(_("Unused")));
- }
- }
-}
-
-}
diff --git a/plugins/omemo/src/ui/contact_details_provider.vala b/plugins/omemo/src/ui/contact_details_provider.vala
index a97a40ad..7c733cd7 100644
--- a/plugins/omemo/src/ui/contact_details_provider.vala
+++ b/plugins/omemo/src/ui/contact_details_provider.vala
@@ -7,6 +7,7 @@ namespace Dino.Plugins.Omemo {
public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object {
public string id { get { return "omemo_info"; } }
+ public string tab { get { return "encryption"; } }
private Plugin plugin;
@@ -14,35 +15,14 @@ public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object {
this.plugin = plugin;
}
- public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) {
- if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK4) {
-
- int identity_id = plugin.db.identity.get_id(conversation.account.id);
- if (identity_id < 0) return;
-
- int i = 0;
- foreach (Row row in plugin.db.identity_meta.with_address(identity_id, conversation.counterpart.to_string())) {
- if (row[plugin.db.identity_meta.identity_key_public_base64] != null) {
- i++;
- }
- }
-
- if (i > 0) {
- Button btn = new Button.from_icon_name("view-list-symbolic") { visible = true, valign = Align.CENTER, has_frame = false };
- btn.tooltip_text = _("OMEMO Key Management");
- btn.clicked.connect(() => {
- btn.activate();
- ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, conversation.account, conversation.counterpart);
- dialog.set_transient_for((Window) btn.get_root());
- dialog.response.connect((response_type) => {
- plugin.device_notification_populator.should_hide();
- });
- dialog.present();
- });
-
- contact_details.add(_("Encryption"), "OMEMO", n("%d OMEMO device", "%d OMEMO devices", i).printf(i), btn);
- }
- }
+ public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { }
+
+ public Object? get_widget(Conversation conversation) {
+ if (conversation.type_ != Conversation.Type.CHAT) return null;
+
+ var widget = new OmemoPreferencesWidget(plugin);
+ widget.set_jid(conversation.account, conversation.counterpart);
+ return widget;
}
}
diff --git a/plugins/omemo/src/ui/device_notification_populator.vala b/plugins/omemo/src/ui/device_notification_populator.vala
index fafe7a24..7829d89c 100644
--- a/plugins/omemo/src/ui/device_notification_populator.vala
+++ b/plugins/omemo/src/ui/device_notification_populator.vala
@@ -35,7 +35,7 @@ public class DeviceNotificationPopulator : NotificationPopulator, Object {
private void display_notification() {
if (notification == null) {
- notification = new ConversationNotification(plugin, current_conversation.account, current_conversation.counterpart);
+ notification = new ConversationNotification(plugin, current_conversation);
notification.should_hide.connect(should_hide);
notification_collection.add_meta_notification(notification);
}
@@ -64,7 +64,7 @@ private class ConversationNotification : MetaConversationNotification {
private Account account;
public signal void should_hide();
- public ConversationNotification(Plugin plugin, Account account, Jid jid) {
+ public ConversationNotification(Plugin plugin, Conversation conversation) {
this.plugin = plugin;
this.jid = jid;
this.account = account;
@@ -73,12 +73,9 @@ private class ConversationNotification : MetaConversationNotification {
Button manage_button = new Button.with_label(_("Manage"));
manage_button.clicked.connect(() => {
manage_button.activate();
- ContactDetailsDialog dialog = new ContactDetailsDialog(plugin, account, jid);
- dialog.set_transient_for((Window) manage_button.get_root());
- dialog.response.connect((response_type) => {
- should_hide();
- });
- dialog.present();
+
+ var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.string("encryption")});
+ GLib.Application.get_default().activate_action("open-conversation-details", variant);
});
box.append(new Label(_("This contact has new devices")) { margin_end=10 });
box.append(manage_button);
diff --git a/plugins/omemo/src/ui/encryption_preferences_entry.vala b/plugins/omemo/src/ui/encryption_preferences_entry.vala
index 71cd68ec..0f9456d9 100644
--- a/plugins/omemo/src/ui/encryption_preferences_entry.vala
+++ b/plugins/omemo/src/ui/encryption_preferences_entry.vala
@@ -19,7 +19,7 @@ public class OmemoPreferencesEntry : Plugins.EncryptionPreferencesEntry {
public override Object? get_widget(Account account, WidgetType type) {
if (type != WidgetType.GTK4) return null;
var widget = new OmemoPreferencesWidget(plugin);
- widget.set_account(account);
+ widget.set_jid(account, account.bare_jid);
return widget;
}
@@ -59,22 +59,24 @@ public class OmemoPreferencesWidget : Adw.PreferencesGroup {
public OmemoPreferencesWidget(Plugin plugin) {
this.plugin = plugin;
- this.account = account;
- this.jid = jid;
}
- public void set_account(Account account) {
+ public void set_jid(Account account, Jid jid) {
this.account = account;
- this.jid = account.bare_jid;
+ this.jid = jid;
+ this.identity_id = plugin.db.identity.get_id(account.id);
+ if (identity_id <= 0) {
+ warning("OmemoPreferencesWidget missing identity_id");
+ return;
+ }
automatically_accept_new_switch.set_active(plugin.db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string(), true));
automatically_accept_new_switch.state_set.connect(on_auto_accept_toggled);
+ encrypt_by_default_row.visible = account.bare_jid.equals_bare(jid);
encrypt_by_default_switch.set_active(plugin.app.settings.get_default_encryption(account) != Encryption.NONE);
encrypt_by_default_switch.state_set.connect(on_omemo_by_default_toggled);
- identity_id = plugin.db.identity.get_id(account.id);
- if (identity_id < 0) return;
Dino.Application? app = Application.get_default() as Dino.Application;
if (app != null) {
store = app.stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).store;
@@ -216,7 +218,7 @@ public class OmemoPreferencesWidget : Adw.PreferencesGroup {
});
});
action_row.activatable = true;
- action_row.title = "Other device";
+ action_row.title = account.bare_jid.equals_bare(jid) ? "Other device" : "Device";
action_row.subtitle = fingerprint_markup(fingerprint_from_base64(key_base64));
string trust_str = _("Accepted");
switch(trust) {
diff --git a/plugins/openpgp/src/contact_details_provider.vala b/plugins/openpgp/src/contact_details_provider.vala
index 9ec84c21..f2fa8f06 100644
--- a/plugins/openpgp/src/contact_details_provider.vala
+++ b/plugins/openpgp/src/contact_details_provider.vala
@@ -6,6 +6,7 @@ namespace Dino.Plugins.OpenPgp {
public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object {
public string id { get { return "pgp_info"; } }
+ public string tab { get { return "encryption"; } }
private StreamInteractor stream_interactor;
@@ -13,23 +14,39 @@ public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object {
this.stream_interactor = stream_interactor;
}
- public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) {
- if (conversation.type_ == Conversation.Type.CHAT && type == WidgetType.GTK4) {
- string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart);
- if (key_id != null) {
- Label label = new Label("") { use_markup=true, justify=Justification.RIGHT, selectable=true };
- Gee.List<GPG.Key>? keys = null;
- try {
- keys = GPGHelper.get_keylist(key_id);
- } catch (Error e) { }
- if (keys != null && keys.size > 0) {
- label.label = markup_colorize_id(keys[0].fpr, true);
- } else {
- label.label = _("Key not in keychain") + "\n" + markup_colorize_id(key_id, false);
- }
- contact_details.add(_("Encryption"), "OpenPGP", "", label);
- }
+ public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { }
+
+ public Object? get_widget(Conversation conversation) {
+ var preferences_group = new Adw.PreferencesGroup() { title="OpenPGP" };
+
+ if (conversation.type_ != Conversation.Type.CHAT) return null;
+
+ string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart);
+ if (key_id == null) return null;
+
+ Gee.List<GPG.Key>? keys = null;
+ try {
+ keys = GPGHelper.get_keylist(key_id);
+ } catch (Error e) { }
+
+ var str = "";
+ if (keys != null && keys.size > 0) {
+ str = markup_id(keys[0].fpr, true);
+ } else {
+ str = _("Key not in keychain") + "\n" + markup_id(key_id, false);
}
+
+ var view = new Adw.ActionRow() {
+ title = "Fingerprint",
+ subtitle = str,
+#if Adw_1_3
+ subtitle_selectable = true,
+#endif
+ };
+
+ preferences_group.add(view);
+
+ return preferences_group;
}
}
diff --git a/plugins/openpgp/src/util.vala b/plugins/openpgp/src/util.vala
index d40cf6ef..9cba9b2f 100644
--- a/plugins/openpgp/src/util.vala
+++ b/plugins/openpgp/src/util.vala
@@ -5,47 +5,16 @@ using Xmpp.Util;
namespace Dino.Plugins.OpenPgp {
-/* Adapted from OpenKeychain */
-public static string markup_colorize_id(string s, bool is_fingerprint) {
+public static string markup_id(string s, bool is_fingerprint) {
string markup = is_fingerprint ? "" : "0x";
for (int i = 0; i < s.length; i += 4) {
string four_chars = s.substring(i, 4).down();
- int raw = (int) from_hex(four_chars);
- uint8[] bytes = {(uint8) ((raw >> 8) & 0xff - 128), (uint8) (raw & 0xff - 128)};
-
- Checksum checksum = new Checksum(ChecksumType.SHA1);
- checksum.update(bytes, bytes.length);
- uint8[] digest = new uint8[20];
- size_t len = 20;
- checksum.get_digest(digest, ref len);
-
- uint8 r = digest[0];
- uint8 g = digest[1];
- uint8 b = digest[2];
-
- if (r == 0 && g == 0 && b == 0) r = g = b = 1;
-
- double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
-
- if (brightness < 80) {
- double factor = 80.0 / brightness;
- r = uint8.min(255, (uint8) (r * factor));
- g = uint8.min(255, (uint8) (g * factor));
- b = uint8.min(255, (uint8) (b * factor));
-
- } else if (brightness > 180) {
- double factor = 180.0 / brightness;
- r = (uint8) (r * factor);
- g = (uint8) (g * factor);
- b = (uint8) (b * factor);
- }
-
if (i == 4 * 5) markup += "\n";
- markup += @"<span foreground=\"$("#%02x%02x%02x".printf(r, g, b))\">$four_chars</span>";
+ markup += four_chars;
if (is_fingerprint) markup += " ";
}
- return "<span font_family='monospace' font='8'>" + markup + "</span>";
+ return "<span font_family='monospace' font='9'>" + markup + "</span>";
}
}