diff options
Diffstat (limited to 'libdino')
| -rw-r--r-- | libdino/src/application.vala | 2 | ||||
| -rw-r--r-- | libdino/src/dbus/notifications.vala | 4 | ||||
| -rw-r--r-- | libdino/src/entity/conversation.vala | 10 | ||||
| -rw-r--r-- | libdino/src/entity/settings.vala | 147 | ||||
| -rw-r--r-- | libdino/src/service/chat_interaction.vala | 18 | ||||
| -rw-r--r-- | libdino/src/service/connection_manager.vala | 3 | ||||
| -rw-r--r-- | libdino/src/service/message_correction.vala | 17 | ||||
| -rw-r--r-- | libdino/src/service/notification_events.vala | 30 |
8 files changed, 206 insertions, 25 deletions
diff --git a/libdino/src/application.vala b/libdino/src/application.vala index f2e3cc9b..13bcd471 100644 --- a/libdino/src/application.vala +++ b/libdino/src/application.vala @@ -19,9 +19,11 @@ public interface Application : GLib.Application { public abstract SearchPathGenerator? search_path_generator { get; set; } internal static string print_xmpp; + internal static bool ignore_tls_errors = false; private const OptionEntry[] options = { { "print-xmpp", 0, 0, OptionArg.STRING, ref print_xmpp, "Print XMPP stanzas identified by DESC to stderr", "DESC" }, + { "ignore-tls-errors", 0, 0, OptionArg.NONE, ref ignore_tls_errors, "Ignore TLS certificate errors", null }, { null } }; diff --git a/libdino/src/dbus/notifications.vala b/libdino/src/dbus/notifications.vala index 400d1da0..fd07b570 100644 --- a/libdino/src/dbus/notifications.vala +++ b/libdino/src/dbus/notifications.vala @@ -7,6 +7,8 @@ namespace Dino { public signal void notification_closed (uint32 id, uint32 reason); + public signal void notification_replied(uint32 id, string text); + public abstract async uint32 notify(string app_name, uint32 replaces_id, string app_icon, string summary, string body, string[] actions, HashTable<string, Variant> hints, int32 expire_timeout) throws DBusError, IOError; @@ -51,4 +53,4 @@ namespace Dino { return ret; } -}
\ No newline at end of file +} diff --git a/libdino/src/entity/conversation.vala b/libdino/src/entity/conversation.vala index 8c1063c7..3853389c 100644 --- a/libdino/src/entity/conversation.vala +++ b/libdino/src/entity/conversation.vala @@ -118,12 +118,20 @@ public class Conversation : Object { } public NotifySetting get_notification_setting(StreamInteractor stream_interactor) { - return notify_setting != NotifySetting.DEFAULT ? notify_setting : get_notification_default_setting(stream_interactor); + return get_notification_setting_for(stream_interactor, Application.get_default().settings.notifications); + } + + public NotifySetting get_notification_setting_for(StreamInteractor stream_interactor, bool notifications_enabled) { + if (!notifications_enabled) return NotifySetting.OFF; + return notify_setting != NotifySetting.DEFAULT ? notify_setting : get_notification_default_setting_for(stream_interactor); } public NotifySetting get_notification_default_setting(StreamInteractor stream_interactor) { if (!Application.get_default().settings.notifications) return NotifySetting.OFF; + return get_notification_default_setting_for(stream_interactor); + } + private NotifySetting get_notification_default_setting_for(StreamInteractor stream_interactor) { if (type_ == Type.GROUPCHAT) { if (stream_interactor.get_module(MucManager.IDENTITY).is_private_room(this.account, this.counterpart)) { return NotifySetting.ON; diff --git a/libdino/src/entity/settings.vala b/libdino/src/entity/settings.vala index c71a8e57..d03bb39f 100644 --- a/libdino/src/entity/settings.vala +++ b/libdino/src/entity/settings.vala @@ -2,6 +2,8 @@ namespace Dino.Entities { public class Settings : Object { + public signal void account_color_changed(Account account, string color); + private Database db; public Settings.from_db(Database db) { @@ -11,8 +13,17 @@ public class Settings : Object { send_marker_ = col_to_bool_or_default("send_marker", true); share_time_ = col_to_bool_or_default("share_time", true); notifications_ = col_to_bool_or_default("notifications", true); + notifications_files_ = col_to_bool_or_default("notifications_files", notifications_); + notifications_calls_ = col_to_bool_or_default("notifications_calls", true); + notifications_subscription_requests_ = col_to_bool_or_default("notifications_subscription_requests", true); + notifications_group_chat_invitations_ = col_to_bool_or_default("notifications_group_chat_invitations", true); + notifications_permission_requests_ = col_to_bool_or_default("notifications_permission_requests", true); + notifications_connection_errors_ = col_to_bool_or_default("notifications_connection_errors", true); + notifications_badge_ = col_to_bool_or_default("notifications_badge", true); convert_utf8_smileys_ = col_to_bool_or_default("convert_utf8_smileys", true); check_spelling = col_to_bool_or_default("check_spelling", true); + avatar_roundness_ = col_to_int_or_default("avatar_roundness", 20).clamp(0, 100); + custom_css_ = db.settings.select({db.settings.value}).with(db.settings.key, "=", "custom_css")[db.settings.value] ?? ""; } private bool col_to_bool_or_default(string key, bool def) { @@ -20,6 +31,12 @@ public class Settings : Object { return val != null ? bool.parse(val) : def; } + private int col_to_int_or_default(string key, int def) { + string? val = db.settings.select({db.settings.value}).with(db.settings.key, "=", key)[db.settings.value]; + int parsed = def; + return val != null && int.try_parse(val, out parsed) ? parsed : def; + } + private bool send_typing_; public bool send_typing { get { return send_typing_; } @@ -68,6 +85,90 @@ public class Settings : Object { } } + private bool notifications_files_; + public bool notifications_files { + get { return notifications_files_; } + set { + db.settings.upsert() + .value(db.settings.key, "notifications_files", true) + .value(db.settings.value, value.to_string()) + .perform(); + notifications_files_ = value; + } + } + + private bool notifications_calls_; + public bool notifications_calls { + get { return notifications_calls_; } + set { + db.settings.upsert() + .value(db.settings.key, "notifications_calls", true) + .value(db.settings.value, value.to_string()) + .perform(); + notifications_calls_ = value; + } + } + + private bool notifications_subscription_requests_; + public bool notifications_subscription_requests { + get { return notifications_subscription_requests_; } + set { + db.settings.upsert() + .value(db.settings.key, "notifications_subscription_requests", true) + .value(db.settings.value, value.to_string()) + .perform(); + notifications_subscription_requests_ = value; + } + } + + private bool notifications_group_chat_invitations_; + public bool notifications_group_chat_invitations { + get { return notifications_group_chat_invitations_; } + set { + db.settings.upsert() + .value(db.settings.key, "notifications_group_chat_invitations", true) + .value(db.settings.value, value.to_string()) + .perform(); + notifications_group_chat_invitations_ = value; + } + } + + private bool notifications_permission_requests_; + public bool notifications_permission_requests { + get { return notifications_permission_requests_; } + set { + db.settings.upsert() + .value(db.settings.key, "notifications_permission_requests", true) + .value(db.settings.value, value.to_string()) + .perform(); + notifications_permission_requests_ = value; + } + } + + private bool notifications_connection_errors_; + public bool notifications_connection_errors { + get { return notifications_connection_errors_; } + set { + db.settings.upsert() + .value(db.settings.key, "notifications_connection_errors", true) + .value(db.settings.value, value.to_string()) + .perform(); + notifications_connection_errors_ = value; + } + } + + private bool notifications_badge_; + public bool notifications_badge { + get { return notifications_badge_; } + set { + db.settings.upsert() + .value(db.settings.key, "notifications_badge", true) + .value(db.settings.value, value.to_string()) + .perform(); + notifications_badge_ = value; + } + } + private bool convert_utf8_smileys_; public bool convert_utf8_smileys { get { return convert_utf8_smileys_; } @@ -93,6 +194,31 @@ public class Settings : Object { } } + private int avatar_roundness_; + public int avatar_roundness { + get { return avatar_roundness_; } + set { + int clamped_value = value.clamp(0, 100); + db.settings.upsert() + .value(db.settings.key, "avatar_roundness", true) + .value(db.settings.value, clamped_value.to_string()) + .perform(); + avatar_roundness_ = clamped_value; + } + } + + private string custom_css_; + public string custom_css { + get { return custom_css_; } + set { + db.settings.upsert() + .value(db.settings.key, "custom_css", true) + .value(db.settings.value, value) + .perform(); + custom_css_ = value; + } + } + public Encryption get_default_encryption(Account account) { string? setting = db.account_settings.get_value(account.id, "default-encryption"); if (setting != null) { @@ -110,6 +236,27 @@ public class Settings : Object { } + + public string get_account_color(Account account) { + string? setting = db.account_settings.get_value(account.id, "conversation-list-color"); + if (setting != null && setting.length > 0) { + return setting; + } + + uint8[] rgb = Xmpp.Xep.ConsistentColor.string_to_rgb(account.bare_jid.to_string()); + return "#%02x%02x%02x".printf(rgb[0], rgb[1], rgb[2]).up(); + } + + public void set_account_color(Account account, string color) { + if (color == get_account_color(account)) return; + + db.account_settings.upsert() + .value(db.account_settings.key, "conversation-list-color", true) + .value(db.account_settings.account_id, account.id, true) + .value(db.account_settings.value, color) + .perform(); + account_color_changed(account, color); + } } } diff --git a/libdino/src/service/chat_interaction.vala b/libdino/src/service/chat_interaction.vala index 1e988382..def9ea86 100644 --- a/libdino/src/service/chat_interaction.vala +++ b/libdino/src/service/chat_interaction.vala @@ -51,6 +51,24 @@ public class ChatInteraction : StreamInteractionModule, Object { return (int) query.count(); } + public async void mark_all_as_read() { + ContentItemStore content_item_store = stream_interactor.get_module(ContentItemStore.IDENTITY); + CounterpartInteractionManager counterpart_interaction_manager = stream_interactor.get_module(CounterpartInteractionManager.IDENTITY); + + foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations()) { + if (get_num_unread(conversation) == 0) continue; + + ContentItem? latest_item = content_item_store.get_latest(conversation); + if (latest_item == null) continue; + + Message? message = content_item_store.get_message_for_content_item(conversation, latest_item); + if (message != null) { + yield send_and_mark_message_displayed(conversation, message); + } + counterpart_interaction_manager.mark_displayed_up_to_content_item(conversation, latest_item); + } + } + public bool is_active_focus(Conversation? conversation = null) { if (conversation != null) { return focus_in && conversation.equals(this.selected_conversation); diff --git a/libdino/src/service/connection_manager.vala b/libdino/src/service/connection_manager.vala index 2ebddd92..afae1da8 100644 --- a/libdino/src/service/connection_manager.vala +++ b/libdino/src/service/connection_manager.vala @@ -384,6 +384,9 @@ public class ConnectionManager : Object { } public static bool on_invalid_certificate(string domain, TlsCertificate peer_cert, TlsCertificateFlags errors) { + if (Application.ignore_tls_errors) { + return true; + } if (domain.has_suffix(".onion") && errors == TlsCertificateFlags.UNKNOWN_CA) { // It's barely possible for .onion servers to provide a non-self-signed cert. // But that's fine because encryption is provided independently though TOR. diff --git a/libdino/src/service/message_correction.vala b/libdino/src/service/message_correction.vala index 55d121e2..5f04bdbc 100644 --- a/libdino/src/service/message_correction.vala +++ b/libdino/src/service/message_correction.vala @@ -59,22 +59,7 @@ public class MessageCorrection : StreamInteractionModule, MessageListener { } public bool is_own_correction_allowed(Conversation conversation, Message message) { - string stanza_id = message.edit_to ?? message.stanza_id; - - Jid? own_jid = null; - if (conversation.type_ == Conversation.Type.CHAT) { - own_jid = stream_interactor.get_account_full_jid(conversation.account); - if (own_jid == null) return false; - Message other = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message_by_sent_resource(conversation, own_jid.resourcepart); - return other != null && other.id == message.id; - } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { - own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); - if (own_jid == null) return false; - return last_messages.has_key(conversation) && - last_messages[conversation].has_key(own_jid) && - last_messages[conversation][own_jid].stanza_id == stanza_id; - } - return false; + return message.direction == Message.DIRECTION_SENT; } private void check_add_correction_node(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { diff --git a/libdino/src/service/notification_events.vala b/libdino/src/service/notification_events.vala index 3e077dfc..d22d54a4 100644 --- a/libdino/src/service/notification_events.vala +++ b/libdino/src/service/notification_events.vala @@ -7,6 +7,7 @@ namespace Dino { public class NotificationEvents : StreamInteractionModule, Object { public static ModuleIdentity<NotificationEvents> IDENTITY = new ModuleIdentity<NotificationEvents>("notification_events"); + private const int MESSAGE_NOTIFICATION_MAX_AGE_SECONDS = 15; public string id { get { return IDENTITY.id; } } public signal void notify_content_item(ContentItem content_item, Conversation conversation); @@ -52,9 +53,6 @@ public class NotificationEvents : StreamInteractionModule, Object { if (item.id == conversation.read_up_to_item) return; if (stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus()) return; - Conversation.NotifySetting notify = conversation.get_notification_setting(stream_interactor); - if (notify == Conversation.NotifySetting.OFF) return; - string conversation_display_name = get_conversation_display_name(stream_interactor, conversation, null); string? participant_display_name = null; if (conversation.type_ == Conversation.Type.GROUPCHAT) { @@ -63,11 +61,16 @@ public class NotificationEvents : StreamInteractionModule, Object { switch (item.type_) { case MessageItem.TYPE: + Conversation.NotifySetting message_notify = conversation.get_notification_setting(stream_interactor); + if (message_notify == Conversation.NotifySetting.OFF) return; + Message message = ((MessageItem) item).message; if (message.direction == Message.DIRECTION_SENT) return; + DateTime? message_time = message.time; + if (message_time != null && message_time.compare(new DateTime.now_utc().add_seconds(-MESSAGE_NOTIFICATION_MAX_AGE_SECONDS)) < 0) return; - if (notify == Conversation.NotifySetting.HIGHLIGHT) { + if (message_notify == Conversation.NotifySetting.HIGHLIGHT) { Jid? nick = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (nick == null) return; @@ -76,21 +79,25 @@ public class NotificationEvents : StreamInteractionModule, Object { } notify_content_item(item, conversation); - if (notify != Conversation.NotifySetting.OFF) { + if (message_notify != Conversation.NotifySetting.OFF) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_message(message, conversation, conversation_display_name, participant_display_name); } break; case FileItem.TYPE: + Conversation.NotifySetting file_notify = conversation.get_notification_setting_for( + stream_interactor, Application.get_default().settings.notifications_files); + if (file_notify == Conversation.NotifySetting.OFF) return; + FileTransfer file_transfer = ((FileItem) item).file_transfer; bool is_image = file_transfer.content_type != null && file_transfer.content_type.is_image(); // Don't notify on file transfers in a groupchat set to "mention only" - if (notify == Conversation.NotifySetting.HIGHLIGHT) return; + if (file_notify == Conversation.NotifySetting.HIGHLIGHT) return; if (file_transfer.direction == FileTransfer.DIRECTION_SENT) return; notify_content_item(item, conversation); - if (notify != Conversation.NotifySetting.OFF) { + if (file_notify != Conversation.NotifySetting.OFF) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_file(file_transfer, conversation, is_image, conversation_display_name, participant_display_name); } @@ -102,6 +109,8 @@ public class NotificationEvents : StreamInteractionModule, Object { } private async void on_voice_request_received(Account account, Jid room_jid, Jid from_jid, string nick) { + if (!Application.get_default().settings.notifications_permission_requests) return; + Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(room_jid, account, Conversation.Type.GROUPCHAT); if (conversation == null) return; @@ -110,6 +119,8 @@ public class NotificationEvents : StreamInteractionModule, Object { } private async void on_received_subscription_request(Jid jid, Account account) { + if (!Application.get_default().settings.notifications_subscription_requests) return; + Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); if (stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus(conversation)) return; @@ -118,6 +129,7 @@ public class NotificationEvents : StreamInteractionModule, Object { } private async void on_call_incoming(Call call, CallState call_state, Conversation conversation, bool video, bool multiparty) { + if (!Application.get_default().settings.notifications_calls) return; if (!stream_interactor.get_module(Calls.IDENTITY).can_we_do_calls(call.account)) return; string conversation_display_name = get_conversation_display_name(stream_interactor, conversation, null); @@ -131,6 +143,8 @@ public class NotificationEvents : StreamInteractionModule, Object { } private async void on_invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason) { + if (!Application.get_default().settings.notifications_group_chat_invitations) return; + string inviter_display_name; if (room_jid.equals_bare(from_jid)) { Conversation conversation = new Conversation(room_jid, account, Conversation.Type.GROUPCHAT); @@ -144,6 +158,8 @@ public class NotificationEvents : StreamInteractionModule, Object { } private async void on_connection_error(Account account, ConnectionManager.ConnectionError error) { + if (!Application.get_default().settings.notifications_connection_errors) return; + NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_connection_error(account, error); } |
