aboutsummaryrefslogtreecommitdiff
path: root/main/src/ui/conversation_content_view
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/ui/conversation_content_view')
-rw-r--r--main/src/ui/conversation_content_view/conversation_item_skeleton.vala2
-rw-r--r--main/src/ui/conversation_content_view/conversation_view.vala106
-rw-r--r--main/src/ui/conversation_content_view/file_default_widget.vala9
-rw-r--r--main/src/ui/conversation_content_view/file_image_widget.vala55
-rw-r--r--main/src/ui/conversation_content_view/file_widget.vala132
-rw-r--r--main/src/ui/conversation_content_view/message_widget.vala3
6 files changed, 208 insertions, 99 deletions
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 00c88db3..3a68c9dc 100644
--- a/main/src/ui/conversation_content_view/conversation_item_skeleton.vala
+++ b/main/src/ui/conversation_content_view/conversation_item_skeleton.vala
@@ -37,7 +37,7 @@ public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface,
private uint time_update_timeout = 0;
private ulong updated_roster_handler_id = 0;
- public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item, bool initial_item) {
+ public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) {
this.stream_interactor = stream_interactor;
this.conversation = conversation;
this.item = item;
diff --git a/main/src/ui/conversation_content_view/conversation_view.vala b/main/src/ui/conversation_content_view/conversation_view.vala
index 5481cfc5..4d978132 100644
--- a/main/src/ui/conversation_content_view/conversation_view.vala
+++ b/main/src/ui/conversation_content_view/conversation_view.vala
@@ -24,7 +24,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
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<ContentMetaItem> content_items = new Gee.TreeSet<ContentMetaItem>(compare_content_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>();
@@ -37,7 +37,6 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
private double? was_page_size;
private Mutex reloading_mutex = Mutex();
- private bool animate = false;
private bool firstLoad = true;
private bool at_current_content = true;
private bool reload_messages = true;
@@ -82,6 +81,15 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
main.add_controller(main_motion_events);
main_motion_events.motion.connect(update_highlight);
+ // Process touch events and capture phase to allow highlighting a message without cursor
+ GestureClick click_controller = new GestureClick();
+ click_controller.touch_only = true;
+ click_controller.propagation_phase = Gtk.PropagationPhase.CAPTURE;
+ main_wrap_box.add_controller(click_controller);
+ click_controller.pressed.connect_after((n, x, y) => {
+ update_highlight(x, y);
+ });
+
return this;
}
@@ -200,6 +208,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
MenuButton button = new MenuButton();
button.icon_name = message_actions[i].icon_name;
button.set_popover(message_actions[i].popover as Popover);
+ button.tooltip_text = Util.string_if_tooltips_active(message_actions[i].tooltip);
action_buttons.add(button);
}
@@ -210,6 +219,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
button.clicked.connect(() => {
message_action.callback(button, current_meta_item, currently_highlighted);
});
+ button.tooltip_text = Util.string_if_tooltips_active(message_actions[i].tooltip);
action_buttons.add(button);
}
}
@@ -232,12 +242,71 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
});
firstLoad = false;
}
+ if (conversation == this.conversation && at_current_content) {
+ // Just make sure we are scrolled down
+ if (scrolled.vadjustment.value != scrolled.vadjustment.upper) {
+ scroll_animation(scrolled.vadjustment.upper).play();
+ }
+ return;
+ }
clear();
initialize_for_conversation_(conversation);
display_latest();
+ at_current_content = true;
+ // Scroll to end
+ scrolled.vadjustment.value = scrolled.vadjustment.upper;
+ }
+
+ private void scroll_and_highlight_item(Plugins.MetaConversationItem target, uint duration = 500) {
+ Widget widget = null;
+ int h = 0;
+ foreach (Plugins.MetaConversationItem item in meta_items) {
+ widget = widgets[item];
+ if (target == item) {
+ break;
+ }
+ h += widget.get_allocated_height();
+ }
+ if (widget != widgets[target]) {
+ warning("Target item widget not reached");
+ return;
+ }
+ double target_height = h - scrolled.vadjustment.page_size * 1/3;
+ Adw.Animation animation = scroll_animation(target_height);
+ animation.done.connect(() => {
+ widget.remove_css_class("highlight-once");
+ widget.add_css_class("highlight-once");
+ Timeout.add(5000, () => {
+ widget.remove_css_class("highlight-once");
+ return false;
+ });
+ });
+ animation.play();
+ }
+
+ private Adw.Animation scroll_animation(double target) {
+#if ADW_1_2
+ return new Adw.TimedAnimation(scrolled, scrolled.vadjustment.value, target, 500,
+ new Adw.PropertyAnimationTarget(scrolled.vadjustment, "value")
+ );
+#else
+ return new Adw.TimedAnimation(scrolled, scrolled.vadjustment.value, target, 500,
+ new Adw.CallbackAnimationTarget(value => {
+ scrolled.vadjustment.value = value;
+ })
+ );
+#endif
+
}
public void initialize_around_message(Conversation conversation, ContentItem content_item) {
+ if (conversation == this.conversation) {
+ ContentMetaItem? matching_item = content_items.first_match(it => it.content_item.id == content_item.id);
+ if (matching_item != null) {
+ scroll_and_highlight_item(matching_item);
+ return;
+ }
+ }
clear();
initialize_for_conversation_(conversation);
Gee.List<ContentMetaItem> before_items = content_populator.populate_before(conversation, content_item, 40);
@@ -245,7 +314,6 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
do_insert_item(item);
}
ContentMetaItem meta_item = content_populator.get_content_meta_item(content_item);
- meta_item.can_merge = false;
Widget w = insert_new(meta_item);
content_items.add(meta_item);
meta_items.add(meta_item);
@@ -261,23 +329,16 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
// Compute where to jump to for centered message, jump, highlight.
reload_messages = false;
Timeout.add(700, () => {
- int h = 0, i = 0;
- foreach (Plugins.MetaConversationItem item in meta_items) {
- Widget widget = widgets[item];
- if (widget == w) {
- break;
- }
- h += widget.get_allocated_height();
- i++;
- }
- scrolled.vadjustment.value = h - scrolled.vadjustment.page_size * 1/3;
- w.add_css_class("highlight-once");
+ scroll_and_highlight_item(meta_item, 300);
reload_messages = true;
return false;
});
}
private void initialize_for_conversation_(Conversation? conversation) {
+ if (this.conversation == conversation) {
+ print("Re-initialized for %s\n", conversation.counterpart.bare_jid.to_string());
+ }
// Deinitialize old conversation
Dino.Application app = Dino.Application.get_default();
if (this.conversation != null) {
@@ -299,9 +360,6 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
}
content_populator.init(this, conversation, Plugins.WidgetType.GTK4);
subscription_notification.init(conversation, this);
-
- animate = false;
- Timeout.add(20, () => { animate = true; return false; });
}
private void display_latest() {
@@ -331,8 +389,8 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
public void do_insert_item(Plugins.MetaConversationItem item) {
lock (meta_items) {
insert_new(item);
- if (item as ContentMetaItem != null) {
- content_items.add(item);
+ if (item is ContentMetaItem) {
+ content_items.add((ContentMetaItem)item);
}
meta_items.add(item);
}
@@ -348,7 +406,9 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
widget_order.remove(skeleton.get_widget());
item_item_skeletons.unset(item);
- content_items.remove(item);
+ if (item is ContentMetaItem) {
+ content_items.remove((ContentMetaItem)item);
+ }
meta_items.remove(item);
}
@@ -387,7 +447,7 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
// Fill datastructure
- ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item, !animate);
+ ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item);
item_item_skeletons[item] = 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());
@@ -503,6 +563,10 @@ public class ConversationView : Widget, Plugins.ConversationItemCollection, Plug
}
}
+ private static int compare_content_meta_items(ContentMetaItem a, ContentMetaItem b) {
+ return compare_meta_items(a, b);
+ }
+
private static int compare_meta_items(Plugins.MetaConversationItem a, Plugins.MetaConversationItem b) {
int cmp1 = a.time.compare(b.time);
if (cmp1 != 0) return cmp1;
diff --git a/main/src/ui/conversation_content_view/file_default_widget.vala b/main/src/ui/conversation_content_view/file_default_widget.vala
index 9efc130f..352c8d7a 100644
--- a/main/src/ui/conversation_content_view/file_default_widget.vala
+++ b/main/src/ui/conversation_content_view/file_default_widget.vala
@@ -10,9 +10,6 @@ namespace Dino.Ui {
public class FileDefaultWidget : Box {
public signal void clicked();
- public signal void open_file();
- public signal void save_file_as();
- public signal void cancel_download();
[GtkChild] public unowned Stack image_stack;
[GtkChild] public unowned Label name_label;
@@ -23,12 +20,6 @@ public class FileDefaultWidget : Box {
private FileTransfer.State state;
- class construct {
- install_action("file.open", null, (widget, action_name) => { ((FileDefaultWidget) widget).open_file(); });
- install_action("file.save_as", null, (widget, action_name) => { ((FileDefaultWidget) widget).save_file_as(); });
- install_action("file.cancel", null, (widget, action_name) => { ((FileDefaultWidget) widget).cancel_download(); });
- }
-
public FileDefaultWidget() {
EventControllerMotion this_motion_events = new EventControllerMotion();
this.add_controller(this_motion_events);
diff --git a/main/src/ui/conversation_content_view/file_image_widget.vala b/main/src/ui/conversation_content_view/file_image_widget.vala
index ec8481b7..505c46a0 100644
--- a/main/src/ui/conversation_content_view/file_image_widget.vala
+++ b/main/src/ui/conversation_content_view/file_image_widget.vala
@@ -8,41 +8,70 @@ namespace Dino.Ui {
public class FileImageWidget : Box {
- FileDefaultWidget file_default_widget;
- FileDefaultWidgetController file_default_widget_controller;
-
public FileImageWidget() {
this.halign = Align.START;
this.add_css_class("file-image-widget");
+ this.set_cursor_from_name("zoom-in");
}
public async void load_from_file(File file, string file_name, int MAX_WIDTH=600, int MAX_HEIGHT=300) throws GLib.Error {
- FixedRatioPicture image = new FixedRatioPicture() { min_width = 100, min_height = 100, max_width = MAX_WIDTH, max_height = MAX_HEIGHT, file = file };
+ Gtk.Box image_overlay_toolbar = new Gtk.Box(Orientation.HORIZONTAL, 0) { halign=Gtk.Align.END, valign=Gtk.Align.START, margin_top=10, margin_start=10, margin_end=10, margin_bottom=10, vexpand=false, visible=false };
+ image_overlay_toolbar.add_css_class("card");
+ image_overlay_toolbar.add_css_class("toolbar");
+ image_overlay_toolbar.add_css_class("overlay-toolbar");
+ image_overlay_toolbar.set_cursor_from_name("default");
+
+ FixedRatioPicture image = new FixedRatioPicture() { min_width=100, min_height=100, max_width=MAX_WIDTH, max_height=MAX_HEIGHT, file=file };
+ GestureClick gesture_click_controller = new GestureClick();
+ gesture_click_controller.button = 1; // listen for left clicks
+ gesture_click_controller.released.connect((n_press, x, y) => {
+ switch (gesture_click_controller.get_device().source) {
+ case Gdk.InputSource.TOUCHSCREEN:
+ case Gdk.InputSource.PEN:
+ if (n_press == 1) {
+ image_overlay_toolbar.visible = !image_overlay_toolbar.visible;
+ } else if (n_press == 2) {
+ this.activate_action("file.open", null);
+ image_overlay_toolbar.visible = false;
+ }
+ break;
+ default:
+ this.activate_action("file.open", null);
+ image_overlay_toolbar.visible = false;
+ break;
+ }
+ });
+ image.add_controller(gesture_click_controller);
FileInfo file_info = file.query_info("*", FileQueryInfoFlags.NONE);
string? mime_type = file_info.get_content_type();
- file_default_widget = new FileDefaultWidget() { valign=Align.END, vexpand=false, visible=false };
- file_default_widget.image_stack.visible = false;
- file_default_widget_controller = new FileDefaultWidgetController(file_default_widget);
- file_default_widget_controller.set_file(file, file_name, mime_type);
+ MenuButton button = new MenuButton();
+ button.icon_name = "open-menu";
+ Menu menu_model = new Menu();
+ menu_model.append(_("Open"), "file.open");
+ menu_model.append(_("Save as…"), "file.save_as");
+ Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model);
+ button.popover = popover_menu;
+
+ image_overlay_toolbar.append(button);
Overlay overlay = new Overlay();
overlay.set_child(image);
- overlay.add_overlay(file_default_widget);
+ overlay.add_overlay(image_overlay_toolbar);
overlay.set_measure_overlay(image, true);
- overlay.set_clip_overlay(file_default_widget, true);
+ overlay.set_clip_overlay(image_overlay_toolbar, true);
EventControllerMotion this_motion_events = new EventControllerMotion();
this.add_controller(this_motion_events);
this_motion_events.enter.connect(() => {
- file_default_widget.visible = true;
+ image_overlay_toolbar.visible = true;
});
this_motion_events.leave.connect(() => {
- if (file_default_widget.file_menu.popover != null && file_default_widget.file_menu.popover.visible) return;
+ if (button.popover != null && button.popover.visible) return;
- file_default_widget.visible = false;
+ image_overlay_toolbar.visible = false;
});
this.append(overlay);
diff --git a/main/src/ui/conversation_content_view/file_widget.vala b/main/src/ui/conversation_content_view/file_widget.vala
index 52a26f33..8c36475a 100644
--- a/main/src/ui/conversation_content_view/file_widget.vala
+++ b/main/src/ui/conversation_content_view/file_widget.vala
@@ -21,7 +21,9 @@ public class FileMetaItem : ConversationSummary.ContentMetaItem {
}
public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) {
- return new FileWidget(stream_interactor, file_transfer);
+ FileWidget widget = new FileWidget(file_transfer);
+ FileWidgetController widget_controller = new FileWidgetController(widget, file_transfer, stream_interactor);
+ return widget;
}
public override Gee.List<Plugins.MessageAction>? get_item_actions(Plugins.WidgetType type) {
@@ -57,7 +59,6 @@ public class FileWidget : SizeRequestBox {
DEFAULT
}
- private StreamInteractor stream_interactor;
private FileTransfer file_transfer;
public FileTransfer.State file_transfer_state { get; set; }
public string file_transfer_mime_type { get; set; }
@@ -66,13 +67,24 @@ public class FileWidget : SizeRequestBox {
private FileDefaultWidgetController default_widget_controller;
private Widget? content = null;
+ public signal void open_file();
+ public signal void save_file_as();
+ public signal void start_download();
+ public signal void cancel_download();
+
+ class construct {
+ install_action("file.open", null, (widget, action_name) => { ((FileWidget) widget).open_file(); });
+ install_action("file.save_as", null, (widget, action_name) => { ((FileWidget) widget).save_file_as(); });
+ install_action("file.download", null, (widget, action_name) => { ((FileWidget) widget).start_download(); });
+ install_action("file.cancel", null, (widget, action_name) => { ((FileWidget) widget).cancel_download(); });
+ }
+
construct {
margin_top = 4;
size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH;
}
- public FileWidget(StreamInteractor stream_interactor, FileTransfer file_transfer) {
- this.stream_interactor = stream_interactor;
+ public FileWidget(FileTransfer file_transfer) {
this.file_transfer = file_transfer;
update_widget.begin();
@@ -113,7 +125,7 @@ public class FileWidget : SizeRequestBox {
if (content != null) this.remove(content);
FileDefaultWidget default_file_widget = new FileDefaultWidget();
default_widget_controller = new FileDefaultWidgetController(default_file_widget);
- default_widget_controller.set_file_transfer(file_transfer, stream_interactor);
+ default_widget_controller.set_file_transfer(file_transfer);
content = default_file_widget;
this.state = State.DEFAULT;
this.append(content);
@@ -138,94 +150,104 @@ public class FileWidget : SizeRequestBox {
}
}
-public class FileDefaultWidgetController : Object {
-
- private FileDefaultWidget widget;
- private FileTransfer? file_transfer;
- public string file_transfer_path { get; set; }
- public string file_transfer_state { get; set; }
- public string file_transfer_mime_type { get; set; }
+public class FileWidgetController : Object {
+ private weak Widget widget;
+ private FileTransfer file_transfer;
private StreamInteractor? stream_interactor;
- private string file_uri;
- private string file_name;
- private FileTransfer.State state;
- public FileDefaultWidgetController(FileDefaultWidget widget) {
+ public FileWidgetController(FileWidget widget, FileTransfer file_transfer, StreamInteractor? stream_interactor = null) {
this.widget = widget;
+ this.ref();
+ this.widget.weak_ref(() => {
+ this.widget = null;
+ this.unref();
+ });
+ this.file_transfer = file_transfer;
+ this.stream_interactor = stream_interactor;
- widget.clicked.connect(on_clicked);
widget.open_file.connect(open_file);
widget.save_file_as.connect(save_file);
+ widget.start_download.connect(start_download);
widget.cancel_download.connect(cancel_download);
}
- public void set_file_transfer(FileTransfer file_transfer, StreamInteractor stream_interactor) {
- this.file_transfer = file_transfer;
- this.stream_interactor = stream_interactor;
-
- widget.name_label.label = file_name = file_transfer.file_name;
-
- file_transfer.bind_property("path", this, "file-transfer-path");
- file_transfer.bind_property("state", this, "file-transfer-state");
- file_transfer.bind_property("mime-type", this, "file-transfer-mime-type");
-
- this.notify["file-transfer-path"].connect(update_file_info);
- this.notify["file-transfer-state"].connect(update_file_info);
- this.notify["file-transfer-mime-type"].connect(update_file_info);
-
- update_file_info();
- }
-
- public void set_file(File file, string file_name, string? mime_type) {
- file_uri = file.get_uri();
- state = FileTransfer.State.COMPLETE;
- widget.name_label.label = this.file_name = file_name;
- widget.update_file_info(mime_type, state, -1);
- }
-
- private void update_file_info() {
- file_uri = file_transfer.get_file().get_uri();
- state = file_transfer.state;
- widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size);
- }
-
private void open_file() {
try{
- AppInfo.launch_default_for_uri(file_uri, null);
+ AppInfo.launch_default_for_uri(file_transfer.get_file().get_uri(), null);
} catch (Error err) {
- warning("Failed to open %s - %s", file_uri, err.message);
+ warning("Failed to open %s - %s", file_transfer.get_file().get_uri(), err.message);
}
}
private void save_file() {
var save_dialog = new FileChooserNative(_("Save as…"), widget.get_root() as Gtk.Window, FileChooserAction.SAVE, null, null);
save_dialog.set_modal(true);
- save_dialog.set_current_name(file_name);
+ save_dialog.set_current_name(file_transfer.file_name);
save_dialog.response.connect(() => {
try{
- GLib.File.new_for_uri(file_uri).copy(save_dialog.get_file(), GLib.FileCopyFlags.OVERWRITE, null);
+ GLib.File.new_for_uri(file_transfer.get_file().get_uri()).copy(save_dialog.get_file(), GLib.FileCopyFlags.OVERWRITE, null);
} catch (Error err) {
- warning("Failed copy file %s - %s", file_uri, err.message);
+ warning("Failed copy file %s - %s", file_transfer.get_file().get_uri(), err.message);
}
});
save_dialog.show();
}
+ private void start_download() {
+ if (stream_interactor != null) {
+ stream_interactor.get_module(FileManager.IDENTITY).download_file.begin(file_transfer);
+ }
+ }
+
private void cancel_download() {
file_transfer.cancellable.cancel();
}
+}
+
+public class FileDefaultWidgetController : Object {
+
+ private FileDefaultWidget widget;
+ private FileTransfer? file_transfer;
+ public string file_transfer_state { get; set; }
+ public string file_transfer_mime_type { get; set; }
+
+ private FileTransfer.State state;
+
+ public FileDefaultWidgetController(FileDefaultWidget widget) {
+ this.widget = widget;
+
+ widget.clicked.connect(on_clicked);
+
+ this.notify["file-transfer-state"].connect(update_file_info);
+ this.notify["file-transfer-mime-type"].connect(update_file_info);
+ }
+
+ public void set_file_transfer(FileTransfer file_transfer) {
+ this.file_transfer = file_transfer;
+
+ widget.name_label.label = file_transfer.file_name;
+
+ file_transfer.bind_property("state", this, "file-transfer-state");
+ file_transfer.bind_property("mime-type", this, "file-transfer-mime-type");
+
+ update_file_info();
+ }
+
+ private void update_file_info() {
+ state = file_transfer.state;
+ widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size);
+ }
private void on_clicked() {
switch (state) {
case FileTransfer.State.COMPLETE:
- open_file();
+ widget.activate_action("file.open", null);
break;
case FileTransfer.State.NOT_STARTED:
- assert(stream_interactor != null && file_transfer != null);
- stream_interactor.get_module(FileManager.IDENTITY).download_file.begin(file_transfer);
+ widget.activate_action("file.download", null);
break;
default:
// Clicking doesn't do anything in FAILED and IN_PROGRESS states
diff --git a/main/src/ui/conversation_content_view/message_widget.vala b/main/src/ui/conversation_content_view/message_widget.vala
index fb4ba162..900525fe 100644
--- a/main/src/ui/conversation_content_view/message_widget.vala
+++ b/main/src/ui/conversation_content_view/message_widget.vala
@@ -217,6 +217,7 @@ public class MessageMetaItem : ContentMetaItem {
if (correction_allowed) {
Plugins.MessageAction action1 = new Plugins.MessageAction();
action1.icon_name = "document-edit-symbolic";
+ action1.tooltip = _("Edit message");
action1.callback = (button, content_meta_item_activated, widget) => {
this.in_edit_mode = true;
};
@@ -225,6 +226,7 @@ public class MessageMetaItem : ContentMetaItem {
Plugins.MessageAction reply_action = new Plugins.MessageAction();
reply_action.icon_name = "mail-reply-sender-symbolic";
+ reply_action.tooltip = _("Reply");
reply_action.callback = (button, content_meta_item_activated, widget) => {
GLib.Application.get_default().activate_action("quote", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(message_item.conversation.id), new GLib.Variant.int32(content_item.id) }));
};
@@ -233,6 +235,7 @@ public class MessageMetaItem : ContentMetaItem {
if (supports_reaction) {
Plugins.MessageAction action2 = new Plugins.MessageAction();
action2.icon_name = "dino-emoticon-add-symbolic";
+ action2.tooltip = _("Add reaction");
EmojiChooser chooser = new EmojiChooser();
chooser.emoji_picked.connect((emoji) => {
stream_interactor.get_module(Reactions.IDENTITY).add_reaction(message_item.conversation, message_item, emoji);