ConversationView: Date separator
This commit is contained in:
parent
1e011852e0
commit
f3ca14f2d6
|
@ -47,7 +47,7 @@ public class Message : Object {
|
|||
public Marked marked {
|
||||
get { return marked_; }
|
||||
set {
|
||||
if (marked == Marked.RECEIVED && marked == Marked.READ) return;
|
||||
if (marked == Marked.RECEIVED && value == Marked.READ) return;
|
||||
marked_ = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,8 +98,8 @@ public abstract class MetaConversationItem : Object {
|
|||
}
|
||||
|
||||
public interface ConversationItemCollection : Object {
|
||||
public abstract void insert_item(MetaConversationItem item);
|
||||
public abstract void remove_item(MetaConversationItem item);
|
||||
public signal void insert_item(MetaConversationItem item);
|
||||
public signal void remove_item(MetaConversationItem item);
|
||||
}
|
||||
|
||||
public interface MessageDisplayProvider : Object {
|
||||
|
|
|
@ -101,6 +101,7 @@ SOURCES
|
|||
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/date_separator_populator.vala
|
||||
src/ui/conversation_summary/default_message_display.vala
|
||||
src/ui/conversation_summary/file_populator.vala
|
||||
src/ui/conversation_summary/image_display.vala
|
||||
|
|
|
@ -67,7 +67,9 @@ public class ConversationItemSkeleton : Grid {
|
|||
|
||||
private void setup(Plugins.MetaConversationItem item) {
|
||||
update_time();
|
||||
if (item.requires_avatar) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -37,9 +37,13 @@ public class ConversationView : Box, Plugins.ConversationItemCollection {
|
|||
|
||||
message_item_populator = new MessagePopulator(stream_interactor);
|
||||
|
||||
insert_item.connect(on_insert_item);
|
||||
remove_item.connect(on_remove_item);
|
||||
|
||||
Application app = GLib.Application.get_default() as Application;
|
||||
app.plugin_registry.register_conversation_item_populator(new ChatStatePopulator(stream_interactor));
|
||||
app.plugin_registry.register_conversation_item_populator(new FilePopulator(stream_interactor));
|
||||
app.plugin_registry.register_conversation_item_populator(new DateSeparatorPopulator(stream_interactor));
|
||||
|
||||
Timeout.add_seconds(60, () => {
|
||||
foreach (ConversationItemSkeleton item_skeleton in item_skeletons) {
|
||||
|
@ -52,6 +56,12 @@ public class ConversationView : Box, Plugins.ConversationItemCollection {
|
|||
}
|
||||
|
||||
public void initialize_for_conversation(Conversation? conversation) {
|
||||
Dino.Application app = Dino.Application.get_default();
|
||||
if (this.conversation != null) {
|
||||
foreach (Plugins.ConversationItemPopulator populator in app.plugin_registry.conversation_item_populators) {
|
||||
populator.close(conversation);
|
||||
}
|
||||
}
|
||||
this.conversation = conversation;
|
||||
stack.set_visible_child_name("void");
|
||||
clear();
|
||||
|
@ -60,7 +70,6 @@ public class ConversationView : Box, Plugins.ConversationItemCollection {
|
|||
animate = false;
|
||||
Timeout.add(20, () => { animate = true; return false; });
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -70,7 +79,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection {
|
|||
stack.set_visible_child_name("main");
|
||||
}
|
||||
|
||||
public void insert_item(Plugins.MetaConversationItem item) {
|
||||
public void on_insert_item(Plugins.MetaConversationItem item) {
|
||||
lock (meta_items) {
|
||||
if (!item.can_merge || !merge_back(item)) {
|
||||
insert_new(item);
|
||||
|
@ -78,7 +87,7 @@ public class ConversationView : Box, Plugins.ConversationItemCollection {
|
|||
}
|
||||
}
|
||||
|
||||
public void remove_item(Plugins.MetaConversationItem item) {
|
||||
public void on_remove_item(Plugins.MetaConversationItem item) {
|
||||
lock (meta_items) {
|
||||
ConversationItemSkeleton? skeleton = item_item_skeletons[item];
|
||||
if (skeleton.items.size > 1) {
|
||||
|
|
107
main/src/ui/conversation_summary/date_separator_populator.vala
Normal file
107
main/src/ui/conversation_summary/date_separator_populator.vala
Normal file
|
@ -0,0 +1,107 @@
|
|||
using Gee;
|
||||
using Gtk;
|
||||
|
||||
using Dino.Entities;
|
||||
using Xmpp;
|
||||
|
||||
namespace Dino.Ui.ConversationSummary {
|
||||
|
||||
class DateSeparatorPopulator : Plugins.ConversationItemPopulator, Object {
|
||||
|
||||
public string id { get { return "date_separator"; } }
|
||||
|
||||
private StreamInteractor stream_interactor;
|
||||
private Conversation? current_conversation;
|
||||
private Plugins.ConversationItemCollection? item_collection;
|
||||
private Gee.TreeSet<DateTime> insert_times;
|
||||
|
||||
|
||||
public DateSeparatorPopulator(StreamInteractor stream_interactor) {
|
||||
this.stream_interactor = stream_interactor;
|
||||
}
|
||||
|
||||
public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) {
|
||||
current_conversation = conversation;
|
||||
this.item_collection = item_collection;
|
||||
item_collection.insert_item.connect(on_insert_item);
|
||||
this.insert_times = new TreeSet<DateTime>((a, b) => {
|
||||
return a.compare(b);
|
||||
});
|
||||
}
|
||||
|
||||
public void close(Conversation conversation) {
|
||||
item_collection.insert_item.disconnect(on_insert_item);
|
||||
}
|
||||
|
||||
public void populate_timespan(Conversation conversation, DateTime after, DateTime before) { }
|
||||
|
||||
public void populate_between_widgets(Conversation conversation, DateTime from, DateTime to) { }
|
||||
|
||||
private void on_insert_item(Plugins.MetaConversationItem item) {
|
||||
if (item.display_time == null) return;
|
||||
|
||||
DateTime time = item.sort_time.to_local();
|
||||
DateTime msg_date = new DateTime.local(time.get_year(), time.get_month(), time.get_day_of_month(), 0, 0, 0);
|
||||
if (!insert_times.contains(msg_date)) {
|
||||
if (insert_times.lower(msg_date) != null) {
|
||||
item_collection.insert_item(new MetaDateItem(msg_date.to_utc()));
|
||||
} else if (insert_times.size > 0) {
|
||||
item_collection.insert_item(new MetaDateItem(insert_times.first().to_utc()));
|
||||
}
|
||||
insert_times.add(msg_date);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MetaDateItem : Plugins.MetaConversationItem {
|
||||
public override DateTime? sort_time { get; set; }
|
||||
|
||||
public override bool can_merge { get; set; default=false; }
|
||||
public override bool requires_avatar { get; set; default=false; }
|
||||
public override bool requires_header { get; set; default=false; }
|
||||
|
||||
private DateTime date;
|
||||
|
||||
public MetaDateItem(DateTime date) {
|
||||
this.date = date;
|
||||
this.sort_time = date;
|
||||
}
|
||||
|
||||
public override Object? get_widget(Plugins.WidgetType widget_type) {
|
||||
Box box = new Box(Orientation.HORIZONTAL, 10) { width_request=300, halign=Align.CENTER, visible=true };
|
||||
box.add(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true });
|
||||
string date_str = get_relative_time(date);
|
||||
Label label = new Label(@"<span size='small'>$date_str</span>") { use_markup=true, halign=Align.CENTER, hexpand=false, visible=true };
|
||||
label.get_style_context().add_class("dim-label");
|
||||
box.add(label);
|
||||
box.add(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true, visible=true });
|
||||
return box;
|
||||
}
|
||||
|
||||
private static string get_relative_time(DateTime time) {
|
||||
DateTime time_local = time.to_local();
|
||||
DateTime now_local = new DateTime.now_local();
|
||||
if (time_local.get_year() == now_local.get_year() &&
|
||||
time_local.get_month() == now_local.get_month() &&
|
||||
time_local.get_day_of_month() == now_local.get_day_of_month()) {
|
||||
return _("Today");
|
||||
}
|
||||
DateTime now_local_minus = now_local.add_days(-1);
|
||||
if (time_local.get_year() == now_local_minus.get_year() &&
|
||||
time_local.get_month() == now_local_minus.get_month() &&
|
||||
time_local.get_day_of_month() == now_local_minus.get_day_of_month()) {
|
||||
return _("Yesterday");
|
||||
}
|
||||
if (time_local.get_year() != now_local.get_year()) {
|
||||
return time_local.format("%x");
|
||||
}
|
||||
TimeSpan timespan = now_local.difference(time_local);
|
||||
if (timespan < 7 * TimeSpan.DAY) {
|
||||
return time_local.format(_("%a, %b %d"));
|
||||
} else {
|
||||
return time_local.format(_("%b %d"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -23,6 +23,7 @@ public class Stanza : Xmpp.Stanza {
|
|||
|
||||
public Stanza.result(Stanza request, StanzaNode? stanza_node = null) {
|
||||
this(request.id);
|
||||
this.to = request.from;
|
||||
this.type_ = TYPE_RESULT;
|
||||
if (stanza_node != null) {
|
||||
stanza.put_node(stanza_node);
|
||||
|
|
|
@ -41,7 +41,6 @@ public class InfoResult {
|
|||
|
||||
public InfoResult(Iq.Stanza iq_request) {
|
||||
iq = new Iq.Stanza.result(iq_request);
|
||||
iq.to = iq_request.from;
|
||||
iq.stanza.put_node(new StanzaNode.build("query", NS_URI_INFO).add_self_xmlns());
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ using Xmpp.Core;
|
|||
namespace Xmpp.Xep.Ping {
|
||||
private const string NS_URI = "urn:xmpp:ping";
|
||||
|
||||
public class Module : XmppStreamModule {
|
||||
public class Module : XmppStreamModule, Iq.Handler {
|
||||
public static ModuleIdentity<Module> IDENTITY = new ModuleIdentity<Module>(NS_URI, "0199_ping");
|
||||
|
||||
public delegate void OnResult(XmppStream stream);
|
||||
|
@ -18,19 +18,20 @@ namespace Xmpp.Xep.Ping {
|
|||
}
|
||||
|
||||
public override void attach(XmppStream stream) {
|
||||
stream.get_module(Iq.Module.IDENTITY).register_for_namespace(NS_URI, new IqHandlerImpl());
|
||||
stream.get_module(Iq.Module.IDENTITY).register_for_namespace(NS_URI, this);
|
||||
stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI);
|
||||
}
|
||||
|
||||
public override void detach(XmppStream stream) { }
|
||||
public override void detach(XmppStream stream) {
|
||||
stream.get_module(Iq.Module.IDENTITY).unregister_from_namespace(NS_URI, this);
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
public override string get_id() { return IDENTITY.id; }
|
||||
|
||||
private class IqHandlerImpl : Iq.Handler, Object {
|
||||
public void on_iq_get(XmppStream stream, Iq.Stanza iq) {
|
||||
stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.result(iq));
|
||||
}
|
||||
public void on_iq_set(XmppStream stream, Iq.Stanza iq) { }
|
||||
}
|
||||
|
||||
public override string get_ns() { return NS_URI; }
|
||||
public override string get_id() { return IDENTITY.id; }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue