aboutsummaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorfiaxh <git@mx.ax.lt>2017-08-27 23:55:49 +0200
committerfiaxh <git@mx.ax.lt>2017-08-28 00:02:59 +0200
commit8bc0d107e740be468ee0c9dcd253de36355088d3 (patch)
tree36858e844d711eb18a68612fd815cb84f4c3a88f /main
parenta807ded65cd907e04bab7b8cd27b5702b157e3a2 (diff)
downloaddino-8bc0d107e740be468ee0c9dcd253de36355088d3.tar.gz
dino-8bc0d107e740be468ee0c9dcd253de36355088d3.zip
Plugins providing conversation items for ConversationView
Diffstat (limited to 'main')
-rw-r--r--main/CMakeLists.txt12
-rw-r--r--main/data/conversation_summary/message_item.ui9
-rw-r--r--main/data/conversation_summary/view.ui2
-rw-r--r--main/src/ui/conversation_summary/chat_state_populator.vala116
-rw-r--r--main/src/ui/conversation_summary/conversation_item.vala32
-rw-r--r--main/src/ui/conversation_summary/conversation_item_skeleton.vala142
-rw-r--r--main/src/ui/conversation_summary/conversation_view.vala173
-rw-r--r--main/src/ui/conversation_summary/default_message_display.vala53
-rw-r--r--main/src/ui/conversation_summary/merged_message_item.vala59
-rw-r--r--main/src/ui/conversation_summary/message_item.vala122
-rw-r--r--main/src/ui/conversation_summary/message_populator.vala66
-rw-r--r--main/src/ui/conversation_summary/message_textview.vala8
-rw-r--r--main/src/ui/conversation_summary/slashme_item.vala44
-rw-r--r--main/src/ui/conversation_summary/slashme_message_display.vala75
-rw-r--r--main/src/ui/conversation_summary/status_item.vala30
-rw-r--r--main/src/ui/conversation_summary/view.vala216
-rw-r--r--main/src/ui/unified_window.vala4
-rw-r--r--main/src/ui/util/helper.vala4
18 files changed, 648 insertions, 519 deletions
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 83c7a6d5..cebd6d4c 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -96,13 +96,13 @@ SOURCES
src/ui/conversation_selector/groupchat_row.vala
src/ui/conversation_selector/list.vala
src/ui/conversation_selector/view.vala
- src/ui/conversation_summary/conversation_item.vala
- src/ui/conversation_summary/merged_message_item.vala
- src/ui/conversation_summary/message_item.vala
+ src/ui/conversation_summary/chat_state_populator.vala
+ src/ui/conversation_summary/conversation_item_skeleton.vala
+ src/ui/conversation_summary/conversation_view.vala
+ src/ui/conversation_summary/default_message_display.vala
+ src/ui/conversation_summary/message_populator.vala
src/ui/conversation_summary/message_textview.vala
- src/ui/conversation_summary/slashme_item.vala
- src/ui/conversation_summary/status_item.vala
- src/ui/conversation_summary/view.vala
+ src/ui/conversation_summary/slashme_message_display.vala
src/ui/conversation_titlebar/encryption_entry.vala
src/ui/conversation_titlebar/menu_entry.vala
src/ui/conversation_titlebar/occupants_entry.vala
diff --git a/main/data/conversation_summary/message_item.ui b/main/data/conversation_summary/message_item.ui
index e7b4f46f..8d53a691 100644
--- a/main/data/conversation_summary/message_item.ui
+++ b/main/data/conversation_summary/message_item.ui
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="DinoUiConversationSummaryMessageItem">
+ <template class="DinoUiConversationSummaryConversationItemSkeleton">
<property name="hexpand">True</property>
<property name="column-spacing">7</property>
<property name="orientation">horizontal</property>
@@ -23,8 +23,9 @@
</child>
<child>
<object class="GtkLabel" id="time_label">
- <property name="visible">True</property>
<property name="xalign">1</property>
+ <property name="valign">start</property>
+ <property name="visible">True</property>
<style>
<class name="dim-label"/>
</style>
@@ -40,6 +41,7 @@
<object class="GtkImage" id="encryption_image">
<property name="visible">False</property>
<property name="xalign">1</property>
+ <property name="valign">start</property>
<style>
<class name="dim-label"/>
</style>
@@ -55,6 +57,7 @@
<object class="GtkImage" id="received_image">
<property name="visible">False</property>
<property name="xalign">1</property>
+ <property name="valign">start</property>
<style>
<class name="dim-label"/>
</style>
@@ -67,4 +70,4 @@
</packing>
</child>
</template>
-</interface> \ No newline at end of file
+</interface>
diff --git a/main/data/conversation_summary/view.ui b/main/data/conversation_summary/view.ui
index 07fb7b71..9139da56 100644
--- a/main/data/conversation_summary/view.ui
+++ b/main/data/conversation_summary/view.ui
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
- <template class="DinoUiConversationSummaryView">
+ <template class="DinoUiConversationSummaryConversationView">
<property name="expand">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
diff --git a/main/src/ui/conversation_summary/chat_state_populator.vala b/main/src/ui/conversation_summary/chat_state_populator.vala
new file mode 100644
index 00000000..06d0cf87
--- /dev/null
+++ b/main/src/ui/conversation_summary/chat_state_populator.vala
@@ -0,0 +1,116 @@
+using Gee;
+using Gtk;
+
+using Dino.Entities;
+using Xmpp;
+
+namespace Dino.Ui.ConversationSummary {
+
+class ChatStatePopulator : Plugins.ConversationItemPopulator, Object {
+
+ public string id { get { return "chat_state"; } }
+
+ private StreamInteractor? stream_interactor;
+ private Conversation? current_conversation;
+ private Plugins.ConversationItemCollection? item_collection;
+
+ private MetaChatStateItem? meta_item;
+
+ public ChatStatePopulator(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+
+ stream_interactor.get_module(CounterpartInteractionManager.IDENTITY).received_state.connect((account, jid, state) => {
+ if (current_conversation != null && current_conversation.account.equals(account) && current_conversation.counterpart.equals_bare(jid)) {
+ Idle.add(() => { update_chat_state(account, jid, state); return false; });
+ }
+ });
+ stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect((message, conversation) => {
+ if (conversation.equals(current_conversation)) {
+ Idle.add(() => { update_chat_state(conversation.account, conversation.counterpart); return false; });
+ }
+ });
+ }
+
+ public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) {
+ current_conversation = conversation;
+ this.item_collection = item_collection;
+ this.meta_item = null;
+
+ update_chat_state(conversation.account, conversation.counterpart);
+ }
+
+ public void close(Conversation conversation) { }
+
+ public void populate_timespan(Conversation conversation, DateTime from, DateTime to) { }
+
+ public void populate_between_widgets(Conversation conversation, DateTime from, DateTime to) { }
+
+ private void update_chat_state(Account account, Jid jid, string? state = null) {
+ string? state_ = state;
+ if (state_ == null) {
+ state_ = stream_interactor.get_module(CounterpartInteractionManager.IDENTITY).get_chat_state(current_conversation.account, current_conversation.counterpart);
+ }
+ string? new_text = null;
+ if (state_ != null) {
+ if (state_ == Xep.ChatStateNotifications.STATE_COMPOSING || state_ == Xep.ChatStateNotifications.STATE_PAUSED) {
+ string display_name = Util.get_display_name(stream_interactor, jid, account);
+ if (state_ == Xep.ChatStateNotifications.STATE_COMPOSING) {
+ new_text = _("is typing...");
+ } else if (state_ == Xep.ChatStateNotifications.STATE_PAUSED) {
+ new_text = _("has stopped typing");
+ }
+ }
+ }
+ if (meta_item != null && new_text == null) {
+ item_collection.remove_item(meta_item);
+ meta_item = null;
+ } else if (meta_item != null && new_text != null) {
+ meta_item.set_text(new_text);
+ } else if (new_text != null) {
+ meta_item = new MetaChatStateItem(stream_interactor, current_conversation, jid, new_text);
+ item_collection.insert_item(meta_item);
+ }
+
+ }
+}
+
+public class MetaChatStateItem : Plugins.MetaConversationItem {
+ public override Jid? jid { get; set; }
+ public override bool dim { get; set; default=true; }
+ public override DateTime? sort_time { get; set; default=new DateTime.now_utc().add_years(10); }
+
+ public override bool can_merge { get; set; default=false; }
+ public override bool requires_avatar { get; set; default=true; }
+ public override bool requires_header { get; set; default=false; }
+
+ private StreamInteractor stream_interactor;
+ private Conversation conversation;
+ private string text;
+ private Label label;
+
+ public MetaChatStateItem(StreamInteractor stream_interactor, Conversation conversation, Jid jid, string text) {
+ this.stream_interactor = stream_interactor;
+ this.conversation = conversation;
+ this.jid = jid;
+ this.text = text;
+ }
+
+ public override Object get_widget(Plugins.WidgetType widget_type) {
+ label = new Label("") { xalign=0, vexpand=true, visible=true };
+ label.get_style_context().add_class("dim-label");
+ update_text();
+ return label;
+ }
+
+ public void set_text(string text) {
+ this.text = text;
+ update_text();
+ }
+
+ private void update_text() {
+ string display_name = Util.get_display_name(stream_interactor, jid, conversation.account);
+ label.label = display_name + " " + text;
+ }
+}
+
+}
diff --git a/main/src/ui/conversation_summary/conversation_item.vala b/main/src/ui/conversation_summary/conversation_item.vala
deleted file mode 100644
index a99025ab..00000000
--- a/main/src/ui/conversation_summary/conversation_item.vala
+++ /dev/null
@@ -1,32 +0,0 @@
-using Dino.Entities;
-
-namespace Dino.Ui.ConversationSummary {
-
-public enum MessageKind {
- TEXT,
- ME_COMMAND
-}
-
-public MessageKind get_message_kind(Message message) {
- if (message.body.has_prefix("/me ")) {
- return MessageKind.ME_COMMAND;
- } else {
- return MessageKind.TEXT;
- }
-}
-
-public interface ConversationItem : Gtk.Widget {
- public abstract bool merge(Entities.Message message);
-
- public static ConversationItem create_for_message(StreamInteractor stream_interactor, Conversation conversation, Message message) {
- switch (get_message_kind(message)) {
- case MessageKind.TEXT:
- return new MergedMessageItem(stream_interactor, conversation, message);
- case MessageKind.ME_COMMAND:
- return new SlashMeItem(stream_interactor, conversation, message);
- }
- assert_not_reached();
- }
-}
-
-} \ No newline at end of file
diff --git a/main/src/ui/conversation_summary/conversation_item_skeleton.vala b/main/src/ui/conversation_summary/conversation_item_skeleton.vala
new file mode 100644
index 00000000..b30d45d3
--- /dev/null
+++ b/main/src/ui/conversation_summary/conversation_item_skeleton.vala
@@ -0,0 +1,142 @@
+using Gee;
+using Gdk;
+using Gtk;
+using Markup;
+
+using Dino.Entities;
+
+namespace Dino.Ui.ConversationSummary {
+
+[GtkTemplate (ui = "/im/dino/conversation_summary/message_item.ui")]
+public class ConversationItemSkeleton : Grid {
+
+ [GtkChild] private Image image;
+ [GtkChild] private Label time_label;
+ [GtkChild] private Image encryption_image;
+ [GtkChild] private Image received_image;
+
+ public StreamInteractor stream_interactor;
+ public Conversation conversation { get; set; }
+ public Gee.List<Plugins.MetaConversationItem> items = new ArrayList<Plugins.MetaConversationItem>();
+
+ private Box box = new Box(Orientation.VERTICAL, 2) { visible=true };
+
+ public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation) {
+ this.conversation = conversation;
+ this.stream_interactor = stream_interactor;
+
+ set_main_widget(box);
+ }
+
+ public void add_meta_item(Plugins.MetaConversationItem item) {
+ items.add(item);
+ if (items.size == 1) {
+ setup(item);
+ }
+ Widget widget = (Widget) item.get_widget(Plugins.WidgetType.GTK);
+ if (item.requires_header) {
+ box.add(widget);
+ } else {
+ set_title_widget(widget);
+ }
+ item.notify["mark"].connect_after(update_received);
+ update_received();
+ }
+
+ public void set_title_widget(Widget w) {
+ attach(w, 1, 0, 1, 1);
+ }
+
+ public void set_main_widget(Widget w) {
+ attach(w, 1, 1, 2, 1);
+ }
+
+ public void update_time() {
+ if (items.size > 0 && items[0].display_time != null) {
+ time_label.label = get_relative_time(items[0].display_time.to_local());
+ }
+ }
+
+ private void setup(Plugins.MetaConversationItem item) {
+ update_time();
+ Util.image_set_from_scaled_pixbuf(image, (new AvatarGenerator(30, 30, image.scale_factor)).set_greyscale(item.dim).draw_jid(stream_interactor, item.jid, conversation.account));
+ if (item.requires_header) {
+ set_default_title_widget(item.jid);
+ }
+ if (item.encryption != null && item.encryption != Encryption.NONE) {
+ encryption_image.visible = true;
+ encryption_image.set_from_icon_name("changes-prevent-symbolic", IconSize.SMALL_TOOLBAR);
+ }
+ }
+
+ private void set_default_title_widget(Jid jid) {
+ Label name_label = new Label("") { use_markup=true, xalign=0, hexpand=true, visible=true };
+ string display_name = Util.get_display_name(stream_interactor, jid, conversation.account);
+ string color = Util.get_name_hex_color(stream_interactor, conversation.account, jid, Util.is_dark_theme(name_label));
+ name_label.label = @"<span foreground=\"#$color\">$display_name</span>";
+ name_label.style_updated.connect(() => {
+ string new_color = Util.get_name_hex_color(stream_interactor, conversation.account, jid, Util.is_dark_theme(name_label));
+ name_label.set_markup(@"<span foreground=\"#$new_color\">$display_name</span>");
+ });
+ set_title_widget(name_label);
+ }
+
+ private void update_received() {
+ bool all_received = true;
+ bool all_read = true;
+ foreach (Plugins.MetaConversationItem item in items) {
+ if (item.mark == Message.Marked.WONTSEND) {
+ received_image.visible = true;
+ received_image.set_from_icon_name("dialog-warning-symbolic", IconSize.SMALL_TOOLBAR);
+ Util.force_error_color(received_image);
+ Util.force_error_color(encryption_image);
+ Util.force_error_color(time_label);
+ return;
+ } else if (item.mark != Message.Marked.READ) {
+ all_read = false;
+ if (item.mark != Message.Marked.RECEIVED) {
+ all_received = false;
+ }
+ }
+ }
+ if (all_read) {
+ received_image.visible = true;
+ received_image.set_from_icon_name("dino-double-tick-symbolic", IconSize.SMALL_TOOLBAR);
+ } else if (all_received) {
+ received_image.visible = true;
+ received_image.set_from_icon_name("dino-tick-symbolic", IconSize.SMALL_TOOLBAR);
+ } else if (received_image.visible) {
+ received_image.set_from_icon_name("image-loading-symbolic", IconSize.SMALL_TOOLBAR);
+ }
+ }
+
+ private static string get_relative_time(DateTime datetime) {
+ DateTime now = new DateTime.now_local();
+ TimeSpan timespan = now.difference(datetime);
+ if (timespan > 365 * TimeSpan.DAY) {
+ return datetime.format(Util.is_24h_format() ?
+ /* xgettext:no-c-format */ /* Date + time in 24h format (w/o seconds) */ _("%x, %H\u2236%M") :
+ /* xgettext:no-c-format */ /* Date + time in 12h format (w/o seconds)*/ _("%x, %l\u2236%M %p"));
+ } else if (timespan > 7 * TimeSpan.DAY) {
+ return datetime.format(Util.is_24h_format() ?
+ /* xgettext:no-c-format */ /* Month, day and time in 24h format (w/o seconds) */ _("%b %d, %H\u2236%M") :
+ /* xgettext:no-c-format */ /* Month, day and time in 12h format (w/o seconds) */ _("%b %d, %l\u2236%M %p"));
+ } else if (datetime.get_day_of_month() != new DateTime.now_utc().get_day_of_month()) {
+ return datetime.format(Util.is_24h_format() ?
+ /* xgettext:no-c-format */ /* Day of week and time in 12h format (w/o seconds) */ _("%a, %H\u2236%M") :
+ /* xgettext:no-c-format */ _("%a, %l\u2236%M %p"));
+ } else if (timespan > 9 * TimeSpan.MINUTE) {
+ return datetime.format(Util.is_24h_format() ?
+ /* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H\u2236%M") :
+ /* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l\u2236%M %p"));
+ } else if (timespan > TimeSpan.MINUTE) {
+ ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE);
+ /* xgettext:this is the beginning of a sentence. */
+ return n("%i min ago", "%i mins ago", mins).printf(mins);
+ } else {
+ return _("Just now");
+ }
+ }
+}
+
+}
diff --git a/main/src/ui/conversation_summary/conversation_view.vala b/main/src/ui/conversation_summary/conversation_view.vala
new file mode 100644
index 00000000..b090e5d7
--- /dev/null
+++ b/main/src/ui/conversation_summary/conversation_view.vala
@@ -0,0 +1,173 @@
+using Gee;
+using Gtk;
+using Pango;
+
+using Dino.Entities;
+
+namespace Dino.Ui.ConversationSummary {
+
+[GtkTemplate (ui = "/im/dino/conversation_summary/view.ui")]
+public class ConversationView : Box, Plugins.ConversationItemCollection {
+
+ public Conversation? conversation { get; private set; }
+
+ [GtkChild] private ScrolledWindow scrolled;
+ [GtkChild] private Box main;
+ [GtkChild] private Stack stack;
+
+ private StreamInteractor stream_interactor;
+ private Gee.TreeSet<Plugins.MetaConversationItem> meta_items = new TreeSet<Plugins.MetaConversationItem>((a, b) => { return a.sort_time.compare(b.sort_time); });
+ private Gee.Map<Plugins.MetaConversationItem, Gee.List<Plugins.MetaConversationItem>> meta_after_items = new Gee.HashMap<Plugins.MetaConversationItem, Gee.List<Plugins.MetaConversationItem>>();
+ 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 MessagePopulator message_item_populator;
+
+ private double? was_value;
+ private double? was_upper;
+ private double? was_page_size;
+
+ private Mutex reloading_mutex = new Mutex();
+ private bool animate = false;
+
+ public ConversationView(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+ scrolled.vadjustment.notify["upper"].connect_after(on_upper_notify);
+ scrolled.vadjustment.notify["value"].connect(on_value_notify);
+
+ message_item_populator = new MessagePopulator(stream_interactor);
+
+ Application app = GLib.Application.get_default() as Application;
+ app.plugin_registry.register_conversation_item_populator(new ChatStatePopulator(stream_interactor));
+
+ Timeout.add_seconds(60, () => {
+ foreach (ConversationItemSkeleton item_skeleton in item_skeletons) {
+ item_skeleton.update_time();
+ }
+ return true;
+ });
+
+ Util.force_base_background(this);
+ }
+
+ public void initialize_for_conversation(Conversation? conversation) {
+ this.conversation = conversation;
+ stack.set_visible_child_name("void");
+ clear();
+ was_upper = null;
+ was_page_size = null;
+ animate = false;
+ Timeout.add(20, () => { animate = true; return false; });
+
+ message_item_populator.init(conversation, this);
+ message_item_populator.populate_number(conversation, new DateTime.now_utc(), 50);
+
+ Dino.Application app = Dino.Application.get_default();
+ foreach (Plugins.ConversationItemPopulator populator in app.plugin_registry.conversation_item_populators) {
+ populator.init(conversation, this, Plugins.WidgetType.GTK);
+ }
+
+ stack.set_visible_child_name("main");
+ }
+
+ public void insert_item(Plugins.MetaConversationItem item) {
+ meta_items.add(item);
+ if (!item.can_merge || !merge_back(item)) {
+ insert_new(item);
+ }
+ }
+
+ public void remove_item(Plugins.MetaConversationItem item) {
+ main.remove(widgets[item]);
+ widgets.unset(item);
+ meta_items.remove(item);
+ item_skeletons.remove(item_item_skeletons[item]);
+ item_item_skeletons.unset(item);
+ }
+
+ private bool merge_back(Plugins.MetaConversationItem item) {
+ Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
+ if (lower_item != null) {
+ ConversationItemSkeleton lower_skeleton = item_item_skeletons[lower_item];
+ Plugins.MetaConversationItem lower_start_item = lower_skeleton.items[0];
+ if (lower_start_item.can_merge &&
+ item.display_time.difference(lower_start_item.display_time) < TimeSpan.MINUTE &&
+ lower_start_item.jid.equals(item.jid) &&
+ lower_start_item.encryption == item.encryption &&
+ item.mark != Message.Marked.WONTSEND) {
+ lower_skeleton.add_meta_item(item);
+ force_alloc_width(lower_skeleton, main.get_allocated_width());
+ item_item_skeletons[item] = lower_skeleton;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void insert_new(Plugins.MetaConversationItem item) {
+ ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation);
+ item_skeleton.add_meta_item(item);
+ item_item_skeletons[item] = item_skeleton;
+ Plugins.MetaConversationItem? lower_item = meta_items.lower(item);
+ int index = lower_item != null ? item_skeletons.index_of(item_item_skeletons[lower_item]) + 1 : 0;
+ item_skeletons.insert(index, item_skeleton);
+
+ Widget insert = item_skeleton;
+ if (animate) {
+ Revealer revealer = new Revealer() {transition_duration = 200, transition_type = RevealerTransitionType.SLIDE_UP, visible = true};
+ revealer.add(item_skeleton);
+ insert = revealer;
+ main.add(insert);
+ revealer.reveal_child = true;
+ } else {
+ main.add(insert);
+ }
+ widgets[item] = insert;
+ force_alloc_width(insert, main.get_allocated_width());
+ main.reorder_child(insert, index);
+ }
+
+ private void on_upper_notify() {
+ if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1 ||
+ scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size
+ scrolled.vadjustment.value = scrolled.vadjustment.upper - scrolled.vadjustment.page_size; // scroll down
+ } else if (scrolled.vadjustment.value < scrolled.vadjustment.upper - scrolled.vadjustment.page_size - 1) {
+ scrolled.vadjustment.value = scrolled.vadjustment.upper - was_upper + scrolled.vadjustment.value; // stay at same content
+ }
+ was_upper = scrolled.vadjustment.upper;
+ was_page_size = scrolled.vadjustment.page_size;
+ reloading_mutex.trylock();
+ reloading_mutex.unlock();
+ }
+
+ private void on_value_notify() {
+ if (scrolled.vadjustment.value < 200) {
+ load_earlier_messages();
+ }
+ }
+
+ private void load_earlier_messages() {
+ was_value = scrolled.vadjustment.value;
+ if (!reloading_mutex.trylock()) return;
+ if (meta_items.size > 0) message_item_populator.populate_number(conversation, meta_items.first().sort_time, 20);
+ }
+
+ // Workaround GTK TextView issues
+ private void force_alloc_width(Widget widget, int width) {
+ Allocation alloc = Allocation();
+ widget.get_preferred_width(out alloc.width, null);
+ widget.get_preferred_height(out alloc.height, null);
+ alloc.width = width;
+ widget.size_allocate(alloc);
+ }
+
+ private void clear() {
+ meta_items.clear();
+ meta_after_items.clear();
+ item_skeletons.clear();
+ item_item_skeletons.clear();
+ main.@foreach((widget) => { main.remove(widget); });
+ }
+}
+
+}
diff --git a/main/src/ui/conversation_summary/default_message_display.vala b/main/src/ui/conversation_summary/default_message_display.vala
new file mode 100644
index 00000000..6082253d
--- /dev/null
+++ b/main/src/ui/conversation_summary/default_message_display.vala
@@ -0,0 +1,53 @@
+using Dino.Entities;
+
+namespace Dino.Ui.ConversationSummary {
+
+public class DefaultMessageDisplay : Plugins.MessageDisplayProvider, Object {
+ public string id { get; set; default="default"; }
+ public double priority { get; set; default=0; }
+
+ public StreamInteractor stream_interactor;
+
+ public DefaultMessageDisplay(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+ }
+
+ public bool can_display(Entities.Message? message) { return true; }
+
+ public Plugins.MetaConversationItem? get_item(Entities.Message message, Conversation conversation) {
+ return new MetaMessageItem(stream_interactor, message, conversation);
+ }
+}
+
+public class MetaMessageItem : Plugins.MetaConversationItem {
+ public override Jid? jid { get; set; }
+ public override DateTime? sort_time { get; set; }
+ public override DateTime? display_time { get; set; }
+ public override Encryption? encryption { get; set; }
+
+ private StreamInteractor stream_interactor;
+ private Conversation conversation;
+ private Message message;
+
+ public MetaMessageItem(StreamInteractor stream_interactor, Message message, Conversation conversation) {
+ this.stream_interactor = stream_interactor;
+ this.conversation = conversation;
+ this.message = message;
+ this.jid = message.from;
+ this.sort_time = message.local_time;
+ this.display_time = message.time;
+ this.encryption = message.encryption;
+ }
+
+ public override bool can_merge { get; set; default=true; }
+ public override bool requires_avatar { get; set; default=true; }
+ public override bool requires_header { get; set; default=true; }
+
+ public override Object get_widget(Plugins.WidgetType widget_type) {
+ MessageTextView text_view = new MessageTextView() { visible = true };
+ text_view.add_text(message.body);
+ return text_view;
+ }
+}
+
+}
diff --git a/main/src/ui/conversation_summary/merged_message_item.vala b/main/src/ui/conversation_summary/merged_message_item.vala
deleted file mode 100644
index 4cabebac..00000000
--- a/main/src/ui/conversation_summary/merged_message_item.vala
+++ /dev/null
@@ -1,59 +0,0 @@
-using Gee;
-using Gdk;
-using Gtk;
-using Markup;
-
-using Dino.Entities;
-
-namespace Dino.Ui.ConversationSummary {
-
-public class MergedMessageItem : MessageItem {
-
- private Label name_label = new Label("") { xalign=0, visible=true, hexpand=true };
- private MessageTextView textview = new MessageTextView() { visible=true };
-
- public MergedMessageItem(StreamInteractor stream_interactor, Conversation conversation, Message message) {
- base(stream_interactor, conversation, message);
- set_main_widget(textview);
- set_title_widget(name_label);
- add_message(message);
-
- string display_name = Util.get_message_display_name(stream_interactor, message, conversation.account);
- string color = Util.get_name_hex_color(stream_interactor, conversation.account, message.from, false);
- name_label.set_markup(@"<span foreground=\"#$color\">$display_name</span>");
-
- textview.style_updated.connect(update_display_style);
- update_display_style();
- }
-
- public override void add_message(Message message) {
- base.add_message(message);
- if (messages.size > 1) textview.add_text("\n");
- string text = message.body;
- if (text.length > 10000) {
- text = text.slice(0, 10000) + " [" + _("Message too long") + "]";
- }
- textview.add_text(text);
- }
-
- public override bool merge(Message message) {
- if (get_message_kind(message) == MessageKind.TEXT &&
- this.from.equals(message.from) &&
- this.messages[0].encryption == message.encryption &&
- message.time.difference(initial_time) < TimeSpan.MINUTE &&
- this.messages[0].marked != Entities.Message.Marked.WONTSEND) {
- add_message(message);
- return true;
- }
- return false;
-
- }
-
- private void update_display_style() {
- string display_name = Util.get_message_display_name(stream_interactor, messages[0], conversation.account);
- string color = Util.get_name_hex_color(stream_interactor, conversation.account, messages[0].real_jid ?? messages[0].from, Util.is_dark_theme(textview));
- name_label.set_markup(@"<span foreground=\"#$color\">$display_name</span>");
- }
-}
-
-}
diff --git a/main/src/ui/conversation_summary/message_item.vala b/main/src/ui/conversation_summary/message_item.vala
deleted file mode 100644
index f669c021..00000000
--- a/main/src/ui/conversation_summary/message_item.vala
+++ /dev/null
@@ -1,122 +0,0 @@
-using Gee;
-using Gdk;
-using Gtk;
-using Markup;
-
-using Dino.Entities;
-
-namespace Dino.Ui.ConversationSummary {
-
-[GtkTemplate (ui = "/im/dino/conversation_summary/message_item.ui")]
-public class MessageItem : Grid, ConversationItem {
-
- [GtkChild] private Image image;
- [GtkChild] private Label time_label;
- [GtkChild] private Image encryption_image;
- [GtkChild] private Image received_image;
-
- public StreamInteractor stream_interactor;
- public Conversation conversation { get; set; }
- public Jid from { get; private set; }
- public DateTime initial_time { get; private set; }
- public ArrayList<Message> messages = new ArrayList<Message>(Message.equals_func);
-
- public MessageItem(StreamInteractor stream_interactor, Conversation conversation, Message message) {
- this.conversation = conversation;
- this.stream_interactor = stream_interactor;
- this.initial_time = message.time;
- this.from = message.from;
-
- if (message.encryption != Encryption.NONE) {
- encryption_image.visible = true;
- encryption_image.set_from_icon_name("changes-prevent-symbolic", IconSize.SMALL_TOOLBAR);
- }
-
- time_label.label = get_relative_time(initial_time.to_local());
- Util.image_set_from_scaled_pixbuf(image, (new AvatarGenerator(30, 30, image.scale_factor)).draw_message(stream_interactor, message));
- }
-
- public void set_title_widget(Widget w) {
- attach(w, 1, 0, 1, 1);
- }
-
- public void set_main_widget(Widget w) {
- attach(w, 1, 1, 2, 1);
- }
-
- public void update() {
- time_label.label = get_relative_time(initial_time.to_local());
- }
-
- public virtual void add_message(Message message) {
- messages.add(message);
-
- message.notify["marked"].connect_after(() => {
- Idle.add(() => { update_received(); return false; });
- });
- update_received();
- }
-
- public virtual bool merge(Message message) {
- return false;
- }
-
- private void update_received() {
- bool all_received = true;
- bool all_read = true;
- foreach (Message message in messages) {
- if (message.marked == Message.Marked.WONTSEND) {
- received_image.visible = true;
- received_image.set_from_icon_name("dialog-warning-symbolic", IconSize.SMALL_TOOLBAR);
- Util.force_error_color(received_image);
- Util.force_error_color(encryption_image);
- Util.force_error_color(time_label);
- return;
- } else if (message.marked != Message.Marked.READ) {
- all_read = false;
- if (message.marked != Message.Marked.RECEIVED) {
- all_received = false;
- }
- }
- }
- if (all_read) {
- received_image.visible = true;
- received_image.set_from_icon_name("dino-double-tick-symbolic", IconSize.SMALL_TOOLBAR);
- } else if (all_received) {
- received_image.visible = true;
- received_image.set_from_icon_name("dino-tick-symbolic", IconSize.SMALL_TOOLBAR);
- } else if (received_image.visible) {
- received_image.set_from_icon_name("image-loading-symbolic", IconSize.SMALL_TOOLBAR);
- }
- }
-
- private static string get_relative_time(DateTime datetime) {
- DateTime now = new DateTime.now_local();
- TimeSpan timespan = now.difference(datetime);
- if (timespan > 365 * TimeSpan.DAY) {
- return datetime.format(Util.is_24h_format() ?
- /* xgettext:no-c-format */ /* Date + time in 24h format (w/o seconds) */ _("%x, %H\u2236%M") :
- /* xgettext:no-c-format */ /* Date + time in 12h format (w/o seconds)*/ _("%x, %l\u2236%M %p"));
- } else if (timespan > 7 * TimeSpan.DAY) {
- return datetime.format(Util.is_24h_format() ?
- /* xgettext:no-c-format */ /* Month, day and time in 24h format (w/o seconds) */ _("%b %d, %H\u2236%M") :
- /* xgettext:no-c-format */ /* Month, day and time in 12h format (w/o seconds) */ _("%b %d, %l\u2236%M %p"));
- } else if (timespan > 1 * TimeSpan.DAY) {
- return datetime.format(Util.is_24h_format() ?
- /* xgettext:no-c-format */ /* Day of week and time in 12h format (w/o seconds) */ _("%a, %H\u2236%M") :
- /* xgettext:no-c-format */ _("%a, %l\u2236%M %p"));
- } else if (timespan > 9 * TimeSpan.MINUTE) {
- return datetime.format(Util.is_24h_format() ?
- /* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H\u2236%M") :
- /* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l\u2236%M %p"));
- } else if (timespan > TimeSpan.MINUTE) {
- ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE);
- /* xgettext:this is the beginning of a sentence. */
- return n("%i min ago", "%i mins ago", mins).printf(mins);
- } else {
- return _("Just now");
- }
- }
-}
-
-}
diff --git a/main/src/ui/conversation_summary/message_populator.vala b/main/src/ui/conversation_summary/message_populator.vala
new file mode 100644
index 00000000..2c3eccd2
--- /dev/null
+++ b/main/src/ui/conversation_summary/message_populator.vala
@@ -0,0 +1,66 @@
+using Gee;
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.ConversationSummary {
+
+public class MessagePopulator : Object {
+
+ private StreamInteractor? stream_interactor;
+ private Conversation? current_conversation;
+ private Plugins.ConversationItemCollection? item_collection;
+
+ public MessagePopulator(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+
+ Application app = GLib.Application.get_default() as Application;
+ app.plugin_registry.register_message_display(new DefaultMessageDisplay(stream_interactor));
+ app.plugin_registry.register_message_display(new SlashmeMessageDisplay(stream_interactor));
+
+
+ stream_interactor.get_module(MessageProcessor.IDENTITY).message_received.connect((message, conversation) => {
+ Idle.add(() => { handle_message(message, conversation); return false; });
+ });
+ stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect((message, conversation) => {
+ Idle.add(() => { handle_message(message, conversation); return false; });
+ });
+ }
+
+ public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection) {
+ current_conversation = conversation;
+ this.item_collection = item_collection;
+ }
+
+ public void close(Conversation conversation) { }
+
+ public void populate_number(Conversation conversation, DateTime from, int n) {
+ Gee.List<Entities.Message>? messages = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_before(conversation, from, n);
+ if (messages != null) {
+ foreach (Entities.Message message in messages) {
+ handle_message(message, conversation);
+ }
+ }
+ }
+
+ private void handle_message(Message message, Conversation conversation) {
+ if (!conversation.equals(current_conversation)) return;
+
+ Plugins.MessageDisplayProvider? best_provider = null;
+ int priority = -1;
+ Application app = GLib.Application.get_default() as Application;
+ foreach (Plugins.MessageDisplayProvider provider in app.plugin_registry.message_displays) {
+ if (provider.can_display(message) && provider.priority > priority) {
+ best_provider = provider;
+ }
+ }
+ Plugins.MetaConversationItem meta_item = best_provider.get_item(message, conversation);
+ meta_item.mark = message.marked;
+ message.notify["marked"].connect(() => {
+ meta_item.mark = message.marked;
+ });
+ item_collection.insert_item(meta_item);
+ }
+}
+
+}
diff --git a/main/src/ui/conversation_summary/message_textview.vala b/main/src/ui/conversation_summary/message_textview.vala
index 80759207..f2a4ca22 100644
--- a/main/src/ui/conversation_summary/message_textview.vala
+++ b/main/src/ui/conversation_summary/message_textview.vala
@@ -27,7 +27,11 @@ public class MessageTextView : TextView {
minimum_width = 0;
}
- public void add_text(string text) {
+ public void add_text(string text_) {
+ string text = text_;
+ if (text.length > 10000) {
+ text = text.slice(0, 10000) + " [" + _("Message too long") + "]";
+ }
TextIter end;
buffer.get_end_iter(out end);
buffer.insert(ref end, text, -1);
@@ -90,4 +94,4 @@ public class MessageTextView : TextView {
}
}
-} \ No newline at end of file
+}
diff --git a/main/src/ui/conversation_summary/slashme_item.vala b/main/src/ui/conversation_summary/slashme_item.vala
deleted file mode 100644
index 2056d2d1..00000000
--- a/main/src/ui/conversation_summary/slashme_item.vala
+++ /dev/null
@@ -1,44 +0,0 @@
-using Gdk;
-using Gtk;
-
-using Dino.Entities;
-
-namespace Dino.Ui.ConversationSummary {
-
-public class SlashMeItem : MessageItem {
-
- private Box box = new Box(Orientation.VERTICAL, 0) { visible=true, vexpand=true };
- private MessageTextView textview = new MessageTextView() { visible=true };
- private string text;
- private TextTag nick_tag;
-
- public SlashMeItem(StreamInteractor stream_interactor, Conversation conversation, Message message) {
- base(stream_interactor, conversation, message);
- box.set_center_widget(textview);
- set_title_widget(box);
- text = message.body.substring(3);
-
- string display_name = Util.get_message_display_name(stream_interactor, message, conversation.account);
- string color = Util.get_name_hex_color(stream_interactor, conversation.account, conversation.counterpart, false);
- nick_tag = textview.buffer.create_tag("nick", foreground: "#" + color);
- TextIter iter;
- textview.buffer.get_start_iter(out iter);
- textview.buffer.insert_with_tags(ref iter, display_name, display_name.length, nick_tag);
- textview.add_text(text);
- add_message(message);
-
- textview.style_updated.connect(update_display_style);
- update_display_style();
- }
-
- public override bool merge(Message message) {
- return false;
- }
-
- private void update_display_style() {
- string color = Util.get_name_hex_color(stream_interactor, conversation.account, messages[0].real_jid ?? messages[0].from, Util.is_dark_theme(textview));
- nick_tag.foreground = "#" + color;
- }
-}
-
-}
diff --git a/main/src/ui/conversation_summary/slashme_message_display.vala b/main/src/ui/conversation_summary/slashme_message_display.vala
new file mode 100644
index 00000000..58d93142
--- /dev/null
+++ b/main/src/ui/conversation_summary/slashme_message_display.vala
@@ -0,0 +1,75 @@
+using Gtk;
+
+using Dino.Entities;
+
+namespace Dino.Ui.ConversationSummary {
+
+public class SlashmeMessageDisplay : Plugins.MessageDisplayProvider, Object {
+ public string id { get; set; default="slashme"; }
+ public double priority { get; set; default=1; }
+
+ public StreamInteractor stream_interactor;
+
+ public SlashmeMessageDisplay(StreamInteractor stream_interactor) {
+ this.stream_interactor = stream_interactor;
+ }
+
+ public bool can_display(Entities.Message? message) {
+ return message.body.has_prefix("/me");
+ }
+
+ public Plugins.MetaConversationItem? get_item(Entities.Message message, Conversation conversation) {
+ return new MetaSlashmeItem(stream_interactor, message, conversation);
+ }
+}
+
+public class MetaSlashmeItem : Plugins.MetaConversationItem {
+ public override Jid? jid { get; set; }
+ public override DateTime? sort_time { get; set; }
+ public override DateTime? display_time { get; set; }
+ public override Encryption? encryption { get; set; }
+
+ private StreamInteractor stream_interactor;
+ private Conversation conversation;
+ private Message message;
+ private TextTag nick_tag;
+ private MessageTextView text_view;
+
+ public MetaSlashmeItem(StreamInteractor stream_interactor, Message message, Conversation conversation) {
+ this.stream_interactor = stream_interactor;
+ this.conversation = conversation;
+ this.message = message;
+ this.jid = message.from;
+ this.sort_time = message.local_time;
+ this.display_time = message.time;
+ this.encryption = message.encryption;
+ }
+
+ public override bool can_merge { get; set; default=false; }
+ public override bool requires_avatar { get; set; default=true; }
+ public override bool requires_header { get; set; default=false; }
+
+ public override Object get_widget(Plugins.WidgetType widget_type) {
+ text_view = new MessageTextView() { valign=Align.CENTER, vexpand=true, visible = true };
+
+ string display_name = Util.get_message_display_name(stream_interactor, message, conversation.account);
+ string color = Util.get_name_hex_color(stream_interactor, conversation.account, conversation.counterpart, Util.is_dark_theme(text_view));
+ nick_tag = text_view.buffer.create_tag("nick", foreground: "#" + color);
+ TextIter iter;
+ text_view.buffer.get_start_iter(out iter);
+ text_view.buffer.insert_with_tags(ref iter, display_name, display_name.length, nick_tag);
+
+ text_view.add_text(message.body.substring(3));
+ text_view.style_updated.connect(update_style);
+ text_view.realize.connect(update_style);
+ return text_view;
+ }
+
+ private void update_style() {
+ string display_name = Util.get_message_display_name(stream_interactor, message, conversation.account);
+ string color = Util.get_name_hex_color(stream_interactor, conversation.account, message.real_jid ?? message.from, Util.is_dark_theme(text_view));
+ nick_tag.foreground = "#" + color;
+ }
+}
+
+}
diff --git a/main/src/ui/conversation_summary/status_item.vala b/main/src/ui/conversation_summary/status_item.vala
deleted file mode 100644
index 1704356c..00000000
--- a/main/src/ui/conversation_summary/status_item.vala
+++ /dev/null
@@ -1,30 +0,0 @@
-using Gtk;
-using Markup;
-
-using Dino.Entities;
-
-namespace Dino.Ui.ConversationSummary {
-
-private class StatusItem : Grid {
-
- private Image image = new Image();
- private Label label = new Label("");
-
- private StreamInteractor stream_interactor;
- private Conversation conversation;
-
- public StatusItem(StreamInteractor stream_interactor, Conversation conversation, string? text) {
- Object(column_spacing : 7);
- set_hexpand(true);
- this.stream_interactor = stream_interactor;
- this.conversation = conversation;
- image.set_from_pixbuf((new AvatarGenerator(30, 30)).set_greyscale(true).draw_conversation(stream_interactor, conversation));
- attach(image, 0, 0, 1, 1);
- attach(label, 1, 0, 1, 1);
- string display_name = Util.get_display_name(stream_interactor, conversation.counterpart, conversation.account);
- label.set_markup(@"<span foreground=\"#B1B1B1\">$(escape_text(display_name)) $text</span>");
- show_all();
- }
-}
-
-} \ No newline at end of file
diff --git a/main/src/ui/conversation_summary/view.vala b/main/src/ui/conversation_summary/view.vala
deleted file mode 100644
index 693f7164..00000000
--- a/main/src/ui/conversation_summary/view.vala
+++ /dev/null
@@ -1,216 +0,0 @@
-using Gee;
-using Gtk;
-using Pango;
-
-using Dino.Entities;
-using Xmpp;
-
-namespace Dino.Ui.ConversationSummary {
-
-[GtkTemplate (ui = "/im/dino/conversation_summary/view.ui")]
-public class View : Box {
-
- public Conversation? conversation { get; private set; }
- public HashMap<Entities.Message, ConversationItem> conversation_items = new HashMap<Entities.Message, ConversationItem>(Entities.Message.hash_func, Entities.Message.equals_func);
-
- [GtkChild] private ScrolledWindow scrolled;
- [GtkChild] private Box main;
- [GtkChild] private Stack stack;
-
- private StreamInteractor stream_interactor;
- private ConversationItem? last_conversation_item;
- private StatusItem typing_status;
- private Entities.Message? earliest_message;
- double? was_value;
- double? was_upper;
- double? was_page_size;
- Object reloading_lock = new Object();
- bool reloading = false;
-
- public View(StreamInteractor stream_interactor) {
- this.stream_interactor = stream_interactor;
- scrolled.vadjustment.notify["upper"].connect_after(on_upper_notify);
- scrolled.vadjustment.notify["value"].connect(on_value_notify);
-
- stream_interactor.get_module(CounterpartInteractionManager.IDENTITY).received_state.connect((account, jid, state) => {
- Idle.add(() => { on_received_state(account, jid, state); return false; });
- });
- stream_interactor.get_module(MessageProcessor.IDENTITY).message_received.connect((message, conversation) => {
- Idle.add(() => { show_message(message, conversation, true); return false; });
- });
- stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect((message, conversation) => {
- Idle.add(() => { show_message(message, conversation, true); return false; });
- });
- stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect((show, jid, account) => {
- Idle.add(() => { on_show_received(show, jid, account); return false; });
- });
- Timeout.add_seconds(60, () => {
- foreach (ConversationItem conversation_item in conversation_items.values) {
- MessageItem message_item = conversation_item as MessageItem;
- if (message_item != null) message_item.update();
- }
- return true;
- });
-
- Util.force_base_background(this);
- }
-
- public void initialize_for_conversation(Conversation? conversation) {
- this.conversation = conversation;
- stack.set_visible_child_name("void");
- clear();
- conversation_items.clear();
- was_upper = null;
- was_page_size = null;
- last_conversation_item = null;
-
- ArrayList<Object> objects = new ArrayList<Object>();
- Gee.List<Entities.Message> messages = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages(conversation);
- if (messages.size > 0) {
- earliest_message = messages[messages.size -1];
- objects.add_all(messages);
- }
- HashMap<Jid, ArrayList<Show>>? shows = stream_interactor.get_module(PresenceManager.IDENTITY).get_shows(conversation.counterpart, conversation.account);
- if (shows != null) {
- foreach (Jid jid in shows.keys) objects.add_all(shows[jid]);
- }
- objects.sort((a, b) => {
- DateTime? dt1 = null;
- DateTime? dt2 = null;
- Entities.Message m1 = a as Entities.Message;
- if (m1 != null) dt1 = m1.time;
- Show s1 = a as Show;
- if (s1 != null) dt1 = s1.datetime;
- Entities.Message m2 = b as Entities.Message;
- if (m2 != null) dt2 = m2.time;
- Show s2 = b as Show;
- if (s2 != null) dt2 = s2.datetime;
- return dt1.compare(dt2);
- });
- foreach (Object o in objects) {
- Entities.Message message = o as Entities.Message;
- Show show = o as Show;
- if (message != null) {
- show_message(message, conversation);
- } else if (show != null) {
- on_show_received(show, conversation.counterpart, conversation.account);
- }
- }
- update_chat_state();
- stack.set_visible_child_name("main");
- }
-
- private void on_received_state(Account account, Jid jid, string state) {
- if (conversation != null && conversation.account.equals(account) && conversation.counterpart.equals_bare(jid)) {
- update_chat_state(state);
- }
- }
-
- private void update_chat_state(string? state = null) {
- string? state_ = state;
- if (state_ == null) {
- state_ = stream_interactor.get_module(CounterpartInteractionManager.IDENTITY).get_chat_state(conversation.account, conversation.counterpart);
- }
- if (typing_status != null) {
- main.remove(typing_status);
- }
- if (state_ != null) {
- if (state_ == Xep.ChatStateNotifications.STATE_COMPOSING || state_ == Xep.ChatStateNotifications.STATE_PAUSED) {
- if (state_ == Xep.ChatStateNotifications.STATE_COMPOSING) {
- typing_status = new StatusItem(stream_interactor, conversation, _("is typing…"));
- } else if (state_ == Xep.ChatStateNotifications.STATE_PAUSED) {
- typing_status = new StatusItem(stream_interactor, conversation, _("has stopped typing"));
- }
- main.add(typing_status);
- }
- }
- }
-
- private void on_show_received(Show show, Jid jid, Account account) {
-
- }
-
- private void on_upper_notify() {
- if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1 ||
- scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size
- scrolled.vadjustment.value = scrolled.vadjustment.upper - scrolled.vadjustment.page_size; // scroll down
- } else if (scrolled.vadjustment.value < scrolled.vadjustment.upper - scrolled.vadjustment.page_size - 1) {
- scrolled.vadjustment.value = scrolled.vadjustment.upper - was_upper + scrolled.vadjustment.value; // stay at same content
- }
- was_upper = scrolled.vadjustment.upper;
- was_page_size = scrolled.vadjustment.page_size;
- lock(reloading_lock) {
- reloading = false;
- }
- }
-
- private void on_value_notify() {
- if (scrolled.vadjustment.value < 200) {
- load_earlier_messages();
- }
- }
-
- private void load_earlier_messages() {
- if (earliest_message == null) return;
-
- was_value = scrolled.vadjustment.value;
- lock(reloading_lock) {
- if(reloading) return;
- reloading = true;
- }
- Gee.List<Entities.Message>? messages = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_before(conversation, earliest_message);
- if (messages != null && messages.size > 0) {
- earliest_message = messages[0];
- MergedMessageItem? current_item = null;
- int items_added = 0;
- for (int i = 0; i < messages.size; i++) {
- if (current_item == null || !current_item.merge(messages[i])) {
- current_item = new MergedMessageItem(stream_interactor, conversation, messages[i]);
- force_alloc_width(current_item, main.get_allocated_width());
- main.add(current_item);
- conversation_items[messages[i]] = current_item;
- main.reorder_child(current_item, items_added);
- items_added++;
- }
- }
- return;
- }
- reloading = false;
- }
-
- private void show_message(Entities.Message message, Conversation conversation, bool animate = false) {
- if (this.conversation != null && this.conversation.equals(conversation)) {
- if (last_conversation_item == null || !last_conversation_item.merge(message)) {
- ConversationItem conversation_item = ConversationItem.create_for_message(stream_interactor, conversation, message);
- if (animate) {
- Revealer revealer = new Revealer() {transition_duration = 200, transition_type = RevealerTransitionType.SLIDE_UP, visible = true};
- revealer.add(conversation_item);
- force_alloc_width(revealer, main.get_allocated_width());
- main.add(revealer);
- revealer.set_reveal_child(true);
- } else {
- force_alloc_width(conversation_item, main.get_allocated_width());
- main.add(conversation_item);
- }
- last_conversation_item = conversation_item;
- }
- conversation_items[message] = last_conversation_item;
- update_chat_state();
- }
- }
-
- // Workaround GTK TextView issues
- private void force_alloc_width(Widget widget, int width) {
- Allocation alloc = Allocation();
- widget.get_preferred_width(out alloc.width, null);
- widget.get_preferred_height(out alloc.height, null);
- alloc.width = width;
- widget.size_allocate(alloc);
- }
-
- private void clear() {
- main.@foreach((widget) => { main.remove(widget); });
- }
-}
-
-}
diff --git a/main/src/ui/unified_window.vala b/main/src/ui/unified_window.vala
index 8244c67a..3a419161 100644
--- a/main/src/ui/unified_window.vala
+++ b/main/src/ui/unified_window.vala
@@ -12,7 +12,7 @@ public class UnifiedWindow : Window {
private ChatInput.View chat_input;
private ConversationListTitlebar conversation_list_titlebar;
private ConversationSelector.View filterable_conversation_list;
- private ConversationSummary.View conversation_frame;
+ private ConversationSummary.ConversationView conversation_frame;
private ConversationTitlebar conversation_titlebar;
private HeaderBar placeholder_headerbar = new HeaderBar() { title="Dino", show_close_button=true, visible=true };
private Paned headerbar_paned = new Paned(Orientation.HORIZONTAL) { visible=true };
@@ -69,7 +69,7 @@ public class UnifiedWindow : Window {
private void setup_unified() {
chat_input = new ChatInput.View(stream_interactor) { visible=true };
- conversation_frame = new ConversationSummary.View(stream_interactor) { visible=true };
+ conversation_frame = new ConversationSummary.ConversationView(stream_interactor) { visible=true };
filterable_conversation_list = new ConversationSelector.View(stream_interactor) { visible=true };
Grid grid = new Grid() { orientation=Orientation.VERTICAL, visible=true };
diff --git a/main/src/ui/util/helper.vala b/main/src/ui/util/helper.vala
index f1355b39..a2dde504 100644
--- a/main/src/ui/util/helper.vala
+++ b/main/src/ui/util/helper.vala
@@ -119,8 +119,8 @@ public static void force_error_color(Gtk.Widget widget, string selector = "*") {
}
public static bool is_dark_theme(Gtk.Widget widget) {
- Gdk.RGBA bg = widget.get_style_context().get_background_color(StateFlags.NORMAL);
- return (bg.red < 0.5 && bg.green < 0.5 && bg.blue < 0.5);
+ Gdk.RGBA bg = widget.get_style_context().get_color(StateFlags.NORMAL);
+ return (bg.red > 0.5 && bg.green > 0.5 && bg.blue > 0.5);
}
public static bool is_24h_format() {