diff options
author | fiaxh <git@lightrise.org> | 2022-10-11 13:37:48 +0200 |
---|---|---|
committer | fiaxh <git@lightrise.org> | 2022-10-11 13:37:48 +0200 |
commit | 80258a874ddfeb87b4b71f5791eab94a2465de6d (patch) | |
tree | 80ef37c3e3c3138d79b3321ee22d185d5a94098e /main | |
parent | 6c6e7e3aa7935ec513b7e5ea9b53a92b741ecf92 (diff) | |
download | dino-80258a874ddfeb87b4b71f5791eab94a2465de6d.tar.gz dino-80258a874ddfeb87b4b71f5791eab94a2465de6d.zip |
Add support for reactions
Diffstat (limited to 'main')
-rw-r--r-- | main/CMakeLists.txt | 2 | ||||
-rw-r--r-- | main/data/conversation_content_view/view.ui | 3 | ||||
-rw-r--r-- | main/data/icons/scalable/actions/dino-emoticon-add-symbolic.svg | 5 | ||||
-rw-r--r-- | main/data/theme.css | 56 | ||||
-rw-r--r-- | main/src/ui/conversation_content_view/conversation_item_skeleton.vala | 10 | ||||
-rw-r--r-- | main/src/ui/conversation_content_view/conversation_view.vala | 87 | ||||
-rw-r--r-- | main/src/ui/conversation_content_view/message_widget.vala | 26 | ||||
-rw-r--r-- | main/src/ui/conversation_content_view/reactions_widget.vala | 192 |
8 files changed, 360 insertions, 21 deletions
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index f5796651..4fc06339 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -17,6 +17,7 @@ set(RESOURCE_LIST dino-conversation-list-placeholder-arrow.svg icons/scalable/actions/dino-account-plus-symbolic.svg + icons/scalable/actions/dino-emoticon-add-symbolic.svg icons/scalable/actions/dino-emoticon-symbolic.svg icons/scalable/actions/dino-qr-code-symbolic.svg @@ -156,6 +157,7 @@ SOURCES src/ui/conversation_content_view/file_image_widget.vala src/ui/conversation_content_view/file_widget.vala src/ui/conversation_content_view/message_widget.vala + src/ui/conversation_content_view/reactions_widget.vala src/ui/conversation_content_view/subscription_notification.vala src/ui/chat_input/chat_input_controller.vala diff --git a/main/data/conversation_content_view/view.ui b/main/data/conversation_content_view/view.ui index d64c0982..a9aae318 100644 --- a/main/data/conversation_content_view/view.ui +++ b/main/data/conversation_content_view/view.ui @@ -36,6 +36,9 @@ <property name="margin-end">10</property> <property name="halign">end</property> <property name="valign">start</property> + <style> + <class name="linked"/> + </style> <child> <object class="GtkButton" id="button1"> <property name="visible">0</property> diff --git a/main/data/icons/scalable/actions/dino-emoticon-add-symbolic.svg b/main/data/icons/scalable/actions/dino-emoticon-add-symbolic.svg new file mode 100644 index 00000000..51cc75c6 --- /dev/null +++ b/main/data/icons/scalable/actions/dino-emoticon-add-symbolic.svg @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg version="1.1" viewBox="0 0 23.568 23.711" xmlns="http://www.w3.org/2000/svg"> + <path d="m10 19.211c2.33 0 4.3-1.46 5.11-3.5h-10.22c0.8 2.04 2.78 3.5 5.11 3.5m-3.5-6.5c0.82843 0 1.5-0.67157 1.5-1.5s-0.67157-1.5-1.5-1.5-1.5 0.67157-1.5 1.5 0.67157 1.5 1.5 1.5m7 0c0.82843 0 1.5-0.67157 1.5-1.5s-0.67157-1.5-1.5-1.5-1.5 0.67157-1.5 1.5 0.67157 1.5 1.5 1.5m-3.5 9c-4.4183 0-8-3.5817-8-8s3.5817-8 8-8c1.4367-0.016553 1.4581-1.9613 0-2-5.53 0-10 4.5-10 10 0 5.5228 4.4772 10 10 10s10-4.4772 10-10c0-1.0544-2-1.0324-2 0 0 4.4183-3.5817 8-8 8"/> + <path d="m18.908 0c-0.33046 0-0.61972 0.12233-0.80078 0.36133-0.18106 0.239-0.25586 0.56747-0.25586 0.97266v2.6523h-2.2715c-0.38896 0-0.70598 0.064394-0.94727 0.21875-0.26371 0.15646-0.38672 0.4669-0.38672 0.83789 0 0.36179 0.12302 0.66715 0.38086 0.83398l0.001953 0.0019531h0.001953c0.24918 0.15228 0.56865 0.21875 0.94922 0.21875h2.2715v2.6543c0 0.40519 0.074767 0.73218 0.25586 0.9707 0.18109 0.23852 0.46892 0.35944 0.79883 0.36133 0.32038 0.001837 0.60667-0.12152 0.78906-0.35937 0.18239-0.23785 0.26374-0.56552 0.26758-0.9707v-0.0019531-2.6543h2.2559c0.41061 0 0.74047-0.073348 0.98242-0.25391s0.36719-0.46969 0.36719-0.80078c0-0.33109-0.12524-0.62218-0.36719-0.80273s-0.57181-0.25391-0.98242-0.25391h-2.2559v-2.6523c0-0.38896-0.064393-0.70598-0.21875-0.94727-0.15646-0.26371-0.46495-0.38672-0.83594-0.38672z"/> +</svg> diff --git a/main/data/theme.css b/main/data/theme.css index b689d96c..c4bc36c3 100644 --- a/main/data/theme.css +++ b/main/data/theme.css @@ -82,6 +82,8 @@ window.dino-main .dino-sidebar > frame { border-bottom: 1px solid @borders; } +/* Message */ + .message-box { transition: background .05s ease; } @@ -107,6 +109,21 @@ window.dino-main .dino-conversation .message-box.error:hover { background: alpha(@error_color, 0.12); } +/* Message Menu */ + +.message-menu-box { + background-color: @theme_base_color; + border: 1px solid alpha(@theme_fg_color, 0.15); + border-radius: 5px; +} + +.message-menu-button { + padding: 6px; + border: none; +} + +/* Fie Widget */ + window.dino-main .file-box-outer, window.dino-main .call-box-outer { background: @theme_base_color; @@ -140,6 +157,8 @@ window.dino-main .file-image-widget .file-box-outer button:hover { background: rgba(100, 100, 100, 0.5); } +/* Call widget */ + window.dino-main .call-box-outer.incoming { border-color: alpha(@theme_selected_bg_color, 0.3); } @@ -153,6 +172,39 @@ window.dino-main .multiparty-participants { background: alpha(@theme_fg_color, 0.04); } +/* Reactions */ + +window.dino-main button.reaction-box, +window.dino-main menubutton.reaction-box > button { + border: 1px solid transparent; + padding: 3px 5px ; + border-radius: 10px; + background-color: alpha(@theme_fg_color, 0.05); + background-image: none; + box-shadow: none; + min-height: 0; + min-width: 0; +} + +window.dino-main button.reaction-box.own-reaction, +window.dino-main menubutton.reaction-box.own-reaction > button { + color: mix(@theme_selected_bg_color, @theme_fg_color, 0.4); + border-color: @theme_selected_bg_color; + background-color: alpha(@theme_selected_bg_color, 0.05); +} + +window.dino-main button.reaction-box:hover, +window.dino-main menubutton.reaction-box:hover > button { + background-color: alpha(@theme_fg_color, 0.1); +} + +window.dino-main button.reaction-box.own-reaction:hover, +window.dino-main menubutton.reaction-box.own-reaction > button { + background-color: alpha(@theme_selected_bg_color, 0.2); +} + +/* Sidebar */ + window.dino-main .dino-sidebar > frame.collapsed { border-bottom: 1px solid @borders; } @@ -165,6 +217,8 @@ window.dino-main .dino-sidebar frame.auto-complete list > row { transition: none; } +/* File overlay */ + window.dino-main .dino-white-overlay { background: @theme_base_color; } @@ -175,6 +229,8 @@ window.dino-main .dino-file-overlay { box-shadow: 0 2px 3px alpha(black, 0.1); } +/* Chat Input*/ + window.dino-main .dino-chatinput frame box { background: transparent; } diff --git a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala index e4e6b804..21aca876 100644 --- a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala +++ b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala @@ -32,6 +32,7 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface, public Entities.Message.Marked item_mark { get; set; } public ContentMetaItem content_meta_item = null; public Widget? widget = null; + private ReactionsController? reactions_controller = null; private uint time_update_timeout = 0; private ulong updated_roster_handler_id = 0; @@ -64,6 +65,15 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface, this.notify["show-skeleton"].connect(update_margin); this.notify["show-skeleton"].connect(set_header); + ContentMetaItem? content_meta_item = item as ContentMetaItem; + if (content_meta_item != null) { + reactions_controller = new ReactionsController(conversation, content_meta_item.content_item, stream_interactor); + reactions_controller.box_activated.connect((widget) => { + main_grid.attach(widget, 1, 2, 4, 1); + }); + reactions_controller.init(); + } + update_margin(); } diff --git a/main/src/ui/conversation_content_view/conversation_view.vala b/main/src/ui/conversation_content_view/conversation_view.vala index 4babbdb4..caeee09a 100644 --- a/main/src/ui/conversation_content_view/conversation_view.vala +++ b/main/src/ui/conversation_content_view/conversation_view.vala @@ -15,19 +15,20 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug [GtkChild] public unowned ScrolledWindow scrolled; [GtkChild] private unowned Revealer notification_revealer; [GtkChild] private unowned Box message_menu_box; - [GtkChild] private unowned Button button1; - [GtkChild] private unowned Image button1_icon; [GtkChild] private unowned Box notifications; [GtkChild] private unowned Box main; [GtkChild] private unowned Box main_wrap_box; [GtkChild] private unowned Stack stack; + private ArrayList<Widget> action_buttons = new ArrayList<Widget>(); + private Gee.List<Dino.Plugins.MessageAction>? message_actions = null; + private StreamInteractor stream_interactor; private Gee.TreeSet<Plugins.MetaConversationItem> content_items = new Gee.TreeSet<Plugins.MetaConversationItem>(compare_meta_items); private Gee.TreeSet<Plugins.MetaConversationItem> meta_items = new TreeSet<Plugins.MetaConversationItem>(compare_meta_items); private Gee.HashMap<Plugins.MetaConversationItem, ConversationItemSkeleton> item_item_skeletons = new Gee.HashMap<Plugins.MetaConversationItem, ConversationItemSkeleton>(); private Gee.HashMap<Plugins.MetaConversationItem, Widget> widgets = new Gee.HashMap<Plugins.MetaConversationItem, Widget>(); - private Gee.List<ConversationItemSkeleton> item_skeletons = new Gee.ArrayList<ConversationItemSkeleton>(); + private Gee.List<Widget> widget_order = new Gee.ArrayList<Widget>(); private ContentProvider content_populator; private SubscriptionNotitication subscription_notification; @@ -81,11 +82,6 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug main.add_controller(main_motion_events); main_motion_events.motion.connect(update_highlight); - button1.clicked.connect(() => { - current_meta_item.get_item_actions(Plugins.WidgetType.GTK4)[0].callback(button1, current_meta_item, currently_highlighted); - update_message_menu(); - }); - return this; } @@ -107,7 +103,20 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug } } + private bool is_highlight_fixed() { + foreach (Widget widget in action_buttons) { + MenuButton? menu_button = widget as MenuButton; + if (menu_button != null && menu_button.popover.visible) return true; + + ToggleButton? toggle_button = widget as ToggleButton; + if (toggle_button != null && toggle_button.active) return true; + } + return false; + } + private void on_leave_notify_event() { + if (is_highlight_fixed()) return; + if (currently_highlighted != null) { currently_highlighted.remove_css_class("highlight"); currently_highlighted = null; @@ -116,6 +125,8 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug } private void update_highlight(double x, double y) { + if (is_highlight_fixed()) return; + if (currently_highlighted != null && (last_y - y).abs() <= 2) { return; } @@ -174,11 +185,42 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug return; } - var actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK4); - message_menu_box.visible = actions != null && actions.size > 0; - if (actions != null && actions.size == 1) { - button1.visible = true; - button1_icon.set_from_icon_name(actions[0].icon_name); + foreach (Widget widget in action_buttons) { + message_menu_box.remove(widget); + } + action_buttons.clear(); + + message_actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK4); + + if (message_actions != null) { + message_menu_box.visible = true; + + // Configure as many buttons as we need with the actions for the current meta item + for (int i = 0; i < message_actions.size; i++) { + if (message_actions[i].popover != null) { + MenuButton button = new MenuButton(); + button.icon_name = message_actions[i].icon_name; + button.set_popover(message_actions[i].popover as Popover); + action_buttons.add(button); + } + + if (message_actions[i].callback != null) { + var message_action = message_actions[i]; + Button button = new Button(); + button.icon_name = message_action.icon_name; + button.clicked.connect(() => { + print(@"$(current_meta_item.jid) skdfj \n"); + message_action.callback(button, current_meta_item, currently_highlighted); + }); + action_buttons.add(button); + } + } + + foreach (Widget widget in action_buttons) { + message_menu_box.append(widget); + } + } else { + message_menu_box.visible = false; } } @@ -309,7 +351,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug if (skeleton != null) { main.remove(skeleton.get_widget()); widgets.unset(item); - item_skeletons.remove(skeleton); + widget_order.remove(skeleton.get_widget()); item_item_skeletons.unset(item); content_items.remove(item); @@ -353,8 +395,8 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug // Fill datastructure ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item, !animate); item_item_skeletons[item] = item_skeleton; - int index = lower_item != null ? item_skeletons.index_of(item_item_skeletons[lower_item]) + 1 : 0; - item_skeletons.insert(index, item_skeleton); + int index = lower_item != null ? widget_order.index_of(item_item_skeletons[lower_item].get_widget()) + 1 : 0; + widget_order.insert(index, item_skeleton.get_widget()); // Insert widget widgets[item] = item_skeleton.get_widget(); @@ -382,7 +424,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug // If an item from the past was added, add everything between that item and the (post-)first present item if (index == 0) { Dino.Application app = Dino.Application.get_default(); - if (item_skeletons.size == 1) { + if (widget_order.size == 1) { foreach (Plugins.ConversationAdditionPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.populate_timespan(conversation, item.time, new DateTime.now_utc()); } @@ -404,6 +446,15 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug (upper_item.mark == Message.Marked.WONTSEND) == (lower_item.mark == Message.Marked.WONTSEND); } + private void on_action_button_clicked(ToggleButton button) { + int button_idx = action_buttons.index_of(button); + print(button_idx.to_string() + "\n"); + Plugins.MessageAction message_action = message_actions[button_idx]; + if (message_action.callback != null) { + message_action.callback(button, current_meta_item, currently_highlighted); + } + } + private void on_upper_notify() { if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size if (at_current_content) { @@ -471,7 +522,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug was_page_size = null; content_items.clear(); meta_items.clear(); - item_skeletons.clear(); + widget_order.clear(); item_item_skeletons.clear(); foreach (Widget widget in widgets.values) { main.remove(widget); diff --git a/main/src/ui/conversation_content_view/message_widget.vala b/main/src/ui/conversation_content_view/message_widget.vala index 3da76226..1f027c89 100644 --- a/main/src/ui/conversation_content_view/message_widget.vala +++ b/main/src/ui/conversation_content_view/message_widget.vala @@ -22,6 +22,7 @@ public class MessageMetaItem : ContentMetaItem { MessageItemEditMode? edit_mode = null; ChatTextViewController? controller = null; + private bool supports_reaction = false; AdditionalInfo additional_info = AdditionalInfo.NONE; ulong realize_id = -1; @@ -35,6 +36,8 @@ public class MessageMetaItem : ContentMetaItem { message_item = content_item as MessageItem; this.stream_interactor = stream_interactor; + init.begin(); + label.activate_link.connect(on_label_activate_link); Message message = ((MessageItem) content_item).message; @@ -68,6 +71,10 @@ public class MessageMetaItem : ContentMetaItem { update_label(); } + private async void init() { + supports_reaction = yield stream_interactor.get_module(Reactions.IDENTITY).conversation_supports_reactions(message_item.conversation); + } + private string generate_markup_text(ContentItem item) { MessageItem message_item = item as MessageItem; Conversation conversation = message_item.conversation; @@ -187,11 +194,13 @@ public class MessageMetaItem : ContentMetaItem { } public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) { - if (content_item as FileItem != null) return null; + if (content_item as FileItem != null || this.in_edit_mode) return null; + if (in_edit_mode) return null; - bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); Gee.List<Plugins.MessageAction> actions = new ArrayList<Plugins.MessageAction>(); - if (allowed && !in_edit_mode) { + + bool correction_allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); + if (correction_allowed) { Plugins.MessageAction action1 = new Plugins.MessageAction(); action1.icon_name = "document-edit-symbolic"; action1.callback = (button, content_meta_item_activated, widget) => { @@ -199,6 +208,17 @@ public class MessageMetaItem : ContentMetaItem { }; actions.add(action1); } + + if (supports_reaction) { + Plugins.MessageAction action2 = new Plugins.MessageAction(); + action2.icon_name = "dino-emoticon-add-symbolic"; + EmojiChooser chooser = new EmojiChooser(); + chooser.emoji_picked.connect((emoji) => { + stream_interactor.get_module(Reactions.IDENTITY).add_reaction(message_item.conversation, message_item, emoji); + }); + action2.popover = chooser; + actions.add(action2); + } return actions; } diff --git a/main/src/ui/conversation_content_view/reactions_widget.vala b/main/src/ui/conversation_content_view/reactions_widget.vala new file mode 100644 index 00000000..83b3204e --- /dev/null +++ b/main/src/ui/conversation_content_view/reactions_widget.vala @@ -0,0 +1,192 @@ +using Gee; +using Gtk; + +using Dino.Entities; +using Xmpp; + +namespace Dino.Ui.ConversationSummary { + +public class ReactionsController : Object { + public signal void box_activated(Widget widget); + + private Conversation conversation; + private Account account; + private ContentItem content_item; + private StreamInteractor stream_interactor; + + private HashMap<string, Gee.List<Jid>> reactions = new HashMap<string, Gee.List<Jid>>(); + + private ReactionsWidget? widget = null; + + public ReactionsController(Conversation conversation, ContentItem content_item, StreamInteractor stream_interactor) { + this.conversation = conversation; + this.account = conversation.account; + this.content_item = content_item; + this.stream_interactor = stream_interactor; + } + + public void init() { + Gee.List<ReactionUsers> reactions = stream_interactor.get_module(Reactions.IDENTITY).get_item_reactions(conversation, content_item); + if (reactions.size > 0) { + initialize_widget(); + } + foreach (ReactionUsers reaction_users in reactions) { + foreach (Jid jid in reaction_users.jids) { + reaction_added(reaction_users.reaction, jid); + } + } + + stream_interactor.get_module(Reactions.IDENTITY).reaction_added.connect((account, content_item_id, jid, reaction) => { + if (this.content_item.id == content_item_id) { + reaction_added(reaction, jid); + } + }); + stream_interactor.get_module(Reactions.IDENTITY).reaction_removed.connect((account, content_item_id, jid, reaction) => { + if (this.content_item.id == content_item_id) { + reaction_removed(reaction, jid); + } + }); + } + + private void initialize_widget() { + widget = new ReactionsWidget() { visible=true }; + widget.emoji_picked.connect((emoji) => { + stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji); + }); + widget.emoji_clicked.connect((emoji) => { + if (account.bare_jid in reactions[emoji]) { + stream_interactor.get_module(Reactions.IDENTITY).remove_reaction(conversation, content_item, emoji); + } else { + stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji); + } + }); + box_activated(widget); + } + + public void reaction_added(string reaction, Jid jid) { + if (widget == null) { + initialize_widget(); + } + + if (!reactions.has_key(reaction)) { + reactions[reaction] = new ArrayList<Jid>(Jid.equals_func); + } + if (jid.equals_bare(account.bare_jid) && reactions[reaction].contains(jid)) { + return; + } + reactions[reaction].add(jid); + + if (reactions[reaction].size == 0) return; + + widget.update_reaction(reaction, reactions[reaction].size, reactions[reaction].contains(account.bare_jid), update_tooltip(reaction)); + } + + public void reaction_removed(string reaction, Jid jid) { + if (!reactions.has_key(reaction)) return; + reactions[reaction].remove(jid); + + if (reactions[reaction].size > 0) { + widget.update_reaction(reaction, reactions[reaction].size, reactions[reaction].contains(account.bare_jid), update_tooltip(reaction)); + } else { + widget.remove_reaction(reaction); + reactions.unset(reaction); + } + + if (reactions.size == 0) { + widget.unparent(); + widget = null; + } + } + + private Gee.List<string> update_tooltip(string reaction) { + var name_list = new ArrayList<string>(); + if (reactions[reaction].size > 0) { + if (account.bare_jid in reactions[reaction]) { + name_list.add(_("You")); + } + foreach (Jid jid in reactions[reaction]) { + if (jid.equals(account.bare_jid)) continue; + + name_list.add(Util.get_participant_display_name(stream_interactor, conversation, jid)); + } + } + return name_list; + } +} + +public class ReactionsWidget : Grid { + + public signal void emoji_picked(string emoji); + public signal void emoji_clicked(string emoji); + + private HashMap<string, Label> reaction_counts = new HashMap<string, Label>(); + private HashMap<string, Button> reaction_buttons = new HashMap<string, Button>(); + private MenuButton add_button; + + public ReactionsWidget() { + this.row_spacing = this.column_spacing = 5; + this.margin_top = 2; + + add_button = new MenuButton() { tooltip_text= _("Add reaction"), visible=true }; + add_button.get_style_context().add_class("reaction-box"); + Image add_image = new Image.from_icon_name("dino-emoticon-add-symbolic") { margin_start=5, margin_end=5, visible=true }; + add_button.set_child(add_image); + + EmojiChooser chooser = new EmojiChooser(); + chooser.emoji_picked.connect((emoji) => { + emoji_picked(emoji); + }); + add_button.set_popover(chooser); + } + + public void update_reaction(string reaction, int count, bool own, Gee.List<string> names) { + if (!reaction_buttons.has_key(reaction)) { + Label reaction_label = new Label("<span size='small'>" + reaction + "</span>") { use_markup=true, visible=true }; + Label count_label = new Label("") { use_markup=true, visible=true }; + Button button = new Button() { visible=true }; + button.get_style_context().add_class("reaction-box"); + Box reaction_box = new Box(Orientation.HORIZONTAL, 4) { visible=true }; + reaction_box.append(reaction_label); + reaction_box.append(count_label); + button.set_child(reaction_box); + + reaction_counts[reaction] = count_label; + reaction_buttons[reaction] = button; + + this.attach(button, (reaction_buttons.size - 1) % 10, (reaction_buttons.size - 1) / 10, 1, 1); + if (add_button.get_parent() != null) this.remove(add_button); + this.attach(add_button, reaction_buttons.size % 10, reaction_buttons.size / 10, 1, 1); + + + button.clicked.connect(() => { + emoji_clicked(reaction); + }); + } + + reaction_counts[reaction].label = "<span size='small'>" + count.to_string() + "</span>"; + if (own) { + reaction_buttons[reaction].get_style_context().add_class("own-reaction"); + } else { + reaction_buttons[reaction].get_style_context().remove_class("own-reaction"); + } + + // Build tooltip + StringBuilder tooltip_builder = new StringBuilder (); + for (int i = 0; i < names.size - 1; i++) { + tooltip_builder.append(names[i]); + if (i < names.size - 2) tooltip_builder.append(", "); + } + if (names.size > 1) { + tooltip_builder.append(" and "); + } + tooltip_builder.append(names[names.size - 1]); + tooltip_builder.append(" reacted with " + reaction); + reaction_buttons[reaction].set_tooltip_text(tooltip_builder.str); + } + + public void remove_reaction(string reaction) { + reaction_buttons[reaction].unparent(); + } +} + +}
\ No newline at end of file |