From 4cc7e076e65c604e7a2f840ddc394ea70700577d Mon Sep 17 00:00:00 2001 From: fiaxh Date: Sat, 2 Mar 2024 13:18:53 +0100 Subject: [PATCH] Add unread indicator Co-authored-by: Alexandre Jousset Co-authored-by: Aidan Epstein --- main/CMakeLists.txt | 1 + main/data/style.css | 7 ++ main/meson.build | 1 + .../conversation_view.vala | 3 + .../unread_indicator_populator.vala | 88 +++++++++++++++++++ 5 files changed, 100 insertions(+) create mode 100644 main/src/ui/conversation_content_view/unread_indicator_populator.vala diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 9a7cb189..ea4de99b 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -192,6 +192,7 @@ SOURCES src/ui/conversation_content_view/quote_widget.vala src/ui/conversation_content_view/reactions_widget.vala src/ui/conversation_content_view/subscription_notification.vala + src/ui/conversation_content_view/unread_indicator_populator.vala src/ui/chat_input/chat_input_controller.vala src/ui/chat_input/chat_text_view.vala diff --git a/main/data/style.css b/main/data/style.css index 5fe3beae..a7a1d8df 100644 --- a/main/data/style.css +++ b/main/data/style.css @@ -282,6 +282,13 @@ window.dino-main .dino-chatinput-button button:checked:backdrop { color: alpha(@theme_unfocused_selected_bg_color, 0.8); } +.dino-unread-line label { + color: @theme_selected_bg_color; +} + +.dino-unread-line separator { + background-color: @theme_selected_bg_color; +} .dino-chatinput, .dino-chatinput textview, diff --git a/main/meson.build b/main/meson.build index fab6ba45..1b5abcfc 100644 --- a/main/meson.build +++ b/main/meson.build @@ -53,6 +53,7 @@ sources = files( 'src/ui/conversation_content_view/quote_widget.vala', 'src/ui/conversation_content_view/reactions_widget.vala', 'src/ui/conversation_content_view/subscription_notification.vala', + 'src/ui/conversation_content_view/unread_indicator_populator.vala', 'src/ui/conversation_details.vala', 'src/ui/conversation_list_titlebar.vala', 'src/ui/conversation_selector/conversation_selector.vala', diff --git a/main/src/ui/conversation_content_view/conversation_view.vala b/main/src/ui/conversation_content_view/conversation_view.vala index 33cb3b22..519aa01f 100644 --- a/main/src/ui/conversation_content_view/conversation_view.vala +++ b/main/src/ui/conversation_content_view/conversation_view.vala @@ -87,6 +87,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug Application app = GLib.Application.get_default() as Application; app.plugin_registry.register_conversation_addition_populator(new ChatStatePopulator(stream_interactor)); app.plugin_registry.register_conversation_addition_populator(new DateSeparatorPopulator(stream_interactor)); + app.plugin_registry.register_conversation_addition_populator(new UnreadIndicatorPopulator(stream_interactor)); // Rather than connecting to the leave event of the main_event_box directly, // we connect to the parent event box that also wraps the overlaying message_menu_box. @@ -382,6 +383,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug foreach (ContentMetaItem item in items) { do_insert_item(item); } + Application app = GLib.Application.get_default() as Application; foreach (Plugins.NotificationPopulator populator in app.plugin_registry.notification_populators) { populator.init(conversation, this, Plugins.WidgetType.GTK4); @@ -398,6 +400,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug return; } } + do_insert_item(item); } diff --git a/main/src/ui/conversation_content_view/unread_indicator_populator.vala b/main/src/ui/conversation_content_view/unread_indicator_populator.vala new file mode 100644 index 00000000..79aa7576 --- /dev/null +++ b/main/src/ui/conversation_content_view/unread_indicator_populator.vala @@ -0,0 +1,88 @@ +using Gee; +using Gtk; + +using Dino.Entities; +using Xmpp; + +namespace Dino.Ui.ConversationSummary { + + class UnreadIndicatorPopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { + + public string id { get { return "unread_indicator"; } } + + private StreamInteractor stream_interactor; + private Conversation? current_conversation; + private UnreadIndicatorItem? unread_indicator = null; + Plugins.ConversationItemCollection item_collection = null; + + public UnreadIndicatorPopulator(StreamInteractor stream_interactor) { + this.stream_interactor = stream_interactor; + + stream_interactor.get_module(ChatInteraction.IDENTITY).focused_out.connect(() => { + update_unread_indicator(); + }); + + stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(() => { + if (!stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus(current_conversation)) { + update_unread_indicator(); + } + }); + } + + private void update_unread_indicator() { + if (current_conversation == null) return; + + ContentItem? read_up_to_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(current_conversation, current_conversation.read_up_to_item); + int current_num_unread = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(current_conversation); + if (current_num_unread == 0 && unread_indicator != null) { + item_collection.remove_item(unread_indicator); + unread_indicator = null; + } + + if (read_up_to_item != null && current_num_unread > 0) { + if (unread_indicator != null) { + item_collection.remove_item(unread_indicator); + } + + unread_indicator = new UnreadIndicatorItem(read_up_to_item); + item_collection.insert_item(unread_indicator); + } + } + + public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { + current_conversation = conversation; + this.item_collection = item_collection; + update_unread_indicator(); + } + + public void close(Conversation conversation) { } + + public void populate_timespan(Conversation conversation, DateTime after, DateTime before) { } + } + + private class UnreadIndicatorItem : Plugins.MetaConversationItem { + public UnreadIndicatorItem(ContentItem after_item) { + this.time = after_item.time; + this.secondary_sort_indicator = int.MAX; + } + + public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { + Box box = new Box(Orientation.HORIZONTAL, 10) { hexpand=true }; + box.get_style_context().add_class("dino-unread-line"); + + Separator sep = new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true }; + box.append(sep); + + Label label = new Label(_("New")) { halign=Align.END, hexpand=false }; + label.attributes = new Pango.AttrList(); + label.attributes.insert(Pango.attr_weight_new(Pango.Weight.BOLD)); + box.append(label); + + return box; + } + + public override Gee.List? get_item_actions(Plugins.WidgetType type) { + return null; + } + } +}