2017-08-27 21:55:49 +00:00
|
|
|
|
using Gee;
|
|
|
|
|
using Gdk;
|
|
|
|
|
using Gtk;
|
|
|
|
|
using Markup;
|
|
|
|
|
|
|
|
|
|
using Dino.Entities;
|
|
|
|
|
|
|
|
|
|
namespace Dino.Ui.ConversationSummary {
|
|
|
|
|
|
2019-06-01 14:00:21 +00:00
|
|
|
|
public class ConversationItemSkeleton : EventBox {
|
2017-08-27 21:55:49 +00:00
|
|
|
|
|
2018-01-16 15:17:42 +00:00
|
|
|
|
private AvatarImage image = new AvatarImage() { margin_top=2, valign=Align.START, visible=true, allow_gray = false };
|
2017-08-27 21:55:49 +00:00
|
|
|
|
|
2019-06-01 14:00:21 +00:00
|
|
|
|
public bool show_skeleton { get; set; }
|
|
|
|
|
public bool last_group_item { get; set; }
|
|
|
|
|
|
2017-08-27 21:55:49 +00:00
|
|
|
|
public StreamInteractor stream_interactor;
|
|
|
|
|
public Conversation conversation { get; set; }
|
2019-06-01 14:00:21 +00:00
|
|
|
|
public Plugins.MetaConversationItem item;
|
2017-08-27 21:55:49 +00:00
|
|
|
|
|
2019-06-01 14:00:21 +00:00
|
|
|
|
private Box image_content_box = new Box(Orientation.HORIZONTAL, 8) { visible=true };
|
|
|
|
|
private Box header_content_box = new Box(Orientation.VERTICAL, 0) { visible=true };
|
2017-11-26 18:28:44 +00:00
|
|
|
|
private DefaultSkeletonHeader default_header;
|
2017-08-27 21:55:49 +00:00
|
|
|
|
|
2017-11-26 18:28:44 +00:00
|
|
|
|
public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) {
|
2017-08-27 21:55:49 +00:00
|
|
|
|
this.conversation = conversation;
|
|
|
|
|
this.stream_interactor = stream_interactor;
|
2019-06-01 14:00:21 +00:00
|
|
|
|
this.get_style_context().add_class("message-box");
|
2017-08-27 21:55:49 +00:00
|
|
|
|
|
2017-11-26 18:28:44 +00:00
|
|
|
|
if (item.requires_avatar) {
|
2018-01-16 15:17:42 +00:00
|
|
|
|
image.set_jid(stream_interactor, item.jid, conversation.account);
|
2018-03-07 16:24:57 +00:00
|
|
|
|
image_content_box.add(image);
|
2017-11-26 18:28:44 +00:00
|
|
|
|
}
|
|
|
|
|
if (item.display_time != null) {
|
|
|
|
|
default_header = new DefaultSkeletonHeader(stream_interactor, conversation, item) { visible=true };
|
|
|
|
|
if (!item.requires_header) {
|
|
|
|
|
default_header.name_label.visible = false;
|
|
|
|
|
default_header.dot_label.visible = false;
|
|
|
|
|
}
|
2019-06-01 14:00:21 +00:00
|
|
|
|
header_content_box.add(default_header);
|
2017-11-26 18:28:44 +00:00
|
|
|
|
}
|
2017-08-27 21:55:49 +00:00
|
|
|
|
|
2019-06-01 14:00:21 +00:00
|
|
|
|
// add item
|
|
|
|
|
this.item = item;
|
2017-11-26 18:28:44 +00:00
|
|
|
|
if (default_header != null) {
|
|
|
|
|
default_header.add_item(item);
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
2017-11-26 18:28:44 +00:00
|
|
|
|
Widget? widget = item.get_widget(Plugins.WidgetType.GTK) as Widget;
|
|
|
|
|
if (widget != null) {
|
2019-06-16 13:17:08 +00:00
|
|
|
|
widget.valign = Align.END;
|
2019-06-01 14:00:21 +00:00
|
|
|
|
header_content_box.add(widget);
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
2017-11-21 21:17:04 +00:00
|
|
|
|
|
2019-06-01 14:00:21 +00:00
|
|
|
|
image_content_box.add(header_content_box);
|
|
|
|
|
this.add(image_content_box);
|
|
|
|
|
|
|
|
|
|
if (item.get_type().is_a(typeof(ContentMetaItem))) {
|
|
|
|
|
this.motion_notify_event.connect((event) => {
|
|
|
|
|
this.set_state_flags(StateFlags.PRELIGHT, false);
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
this.enter_notify_event.connect((event) => {
|
|
|
|
|
this.set_state_flags(StateFlags.PRELIGHT, false);
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
this.leave_notify_event.connect((event) => {
|
|
|
|
|
this.unset_state_flags(StateFlags.PRELIGHT);
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.notify["show-skeleton"].connect(update_margin);
|
|
|
|
|
this.notify["last-group-item"].connect(update_margin);
|
|
|
|
|
|
|
|
|
|
this.show_skeleton = true;
|
|
|
|
|
this.last_group_item = true;
|
|
|
|
|
update_margin();
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void update_time() {
|
2017-11-26 18:28:44 +00:00
|
|
|
|
if (default_header != null) {
|
|
|
|
|
default_header.update_time();
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-01 14:00:21 +00:00
|
|
|
|
|
|
|
|
|
public void update_margin() {
|
|
|
|
|
image.visible = this.show_skeleton;
|
|
|
|
|
if (default_header != null) {
|
|
|
|
|
default_header.visible = this.show_skeleton;
|
|
|
|
|
}
|
|
|
|
|
image_content_box.margin_start = this.show_skeleton ? 15 : 58;
|
|
|
|
|
|
|
|
|
|
if (this.show_skeleton && this.last_group_item) {
|
|
|
|
|
image_content_box.margin_top = 8;
|
|
|
|
|
image_content_box.margin_bottom = 8;
|
|
|
|
|
} else {
|
|
|
|
|
image_content_box.margin_top = 4;
|
|
|
|
|
image_content_box.margin_bottom = 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-11-26 18:28:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class DefaultSkeletonHeader : Box {
|
|
|
|
|
private Box box = new Box(Orientation.HORIZONTAL, 4) { visible=true };
|
2019-06-01 14:00:21 +00:00
|
|
|
|
public Label name_label = new Label("") { use_markup=true, valign=Align.START, xalign=0, visible=true };
|
|
|
|
|
public Label time_label = new Label("") { use_markup=true, valign=Align.START, xalign=0, visible=true };
|
|
|
|
|
public Label dot_label = new Label("<span size='small'>·</span>") { use_markup=true, valign=Align.START, xalign=0, visible=true };
|
2017-11-26 18:28:44 +00:00
|
|
|
|
public Image encryption_image = new Image();
|
|
|
|
|
public Image received_image = new Image();
|
|
|
|
|
|
|
|
|
|
private StreamInteractor stream_interactor;
|
|
|
|
|
private Conversation conversation;
|
|
|
|
|
private Plugins.MetaConversationItem item;
|
|
|
|
|
private ArrayList<Plugins.MetaConversationItem> items = new ArrayList<Plugins.MetaConversationItem>();
|
|
|
|
|
|
|
|
|
|
public static IconSize ICON_SIZE_HEADER = Gtk.icon_size_register("im.dino.Dino.HEADER_ICON", 17, 12);
|
|
|
|
|
public virtual string TEXT_SIZE { get { return "small"; } }
|
|
|
|
|
|
|
|
|
|
construct {
|
|
|
|
|
time_label.get_style_context().add_class("dim-label");
|
|
|
|
|
dot_label.get_style_context().add_class("dim-label");
|
|
|
|
|
encryption_image.opacity = 0.4;
|
|
|
|
|
received_image.opacity = 0.4;
|
|
|
|
|
}
|
2017-08-27 21:55:49 +00:00
|
|
|
|
|
2017-11-26 18:28:44 +00:00
|
|
|
|
public DefaultSkeletonHeader(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) {
|
|
|
|
|
this.stream_interactor = stream_interactor;
|
|
|
|
|
this.conversation = conversation;
|
|
|
|
|
this.item = item;
|
|
|
|
|
|
|
|
|
|
box.add(name_label);
|
|
|
|
|
box.add(dot_label);
|
|
|
|
|
box.add(time_label);
|
|
|
|
|
box.add(received_image);
|
|
|
|
|
box.add(encryption_image);
|
|
|
|
|
this.add(box);
|
|
|
|
|
|
|
|
|
|
update_name_label();
|
|
|
|
|
name_label.style_updated.connect(update_name_label);
|
2017-08-27 21:55:49 +00:00
|
|
|
|
if (item.encryption != null && item.encryption != Encryption.NONE) {
|
|
|
|
|
encryption_image.visible = true;
|
2017-11-26 18:28:44 +00:00
|
|
|
|
encryption_image.set_from_icon_name("dino-changes-prevent-symbolic", ICON_SIZE_HEADER);
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
2017-11-26 18:28:44 +00:00
|
|
|
|
update_time();
|
|
|
|
|
add_item(item);
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-26 18:28:44 +00:00
|
|
|
|
public void add_item(Plugins.MetaConversationItem item) {
|
|
|
|
|
items.add(item);
|
|
|
|
|
item.notify["mark"].connect_after(update_received_mark);
|
|
|
|
|
update_received_mark();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void update_time() {
|
|
|
|
|
if (item.display_time != null) {
|
|
|
|
|
time_label.label = @"<span size='$TEXT_SIZE'>" + get_relative_time(item.display_time.to_local()) + "</span>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void update_name_label() {
|
|
|
|
|
string display_name = Util.get_display_name(stream_interactor, item.jid, conversation.account);
|
|
|
|
|
string color = Util.get_name_hex_color(stream_interactor, conversation.account, item.jid, Util.is_dark_theme(name_label));
|
|
|
|
|
name_label.label = @"<span size='$TEXT_SIZE' foreground=\"#$color\">$display_name</span>";
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-21 21:17:04 +00:00
|
|
|
|
private void update_received_mark() {
|
2017-08-27 21:55:49 +00:00
|
|
|
|
bool all_received = true;
|
|
|
|
|
bool all_read = true;
|
2017-08-29 22:03:37 +00:00
|
|
|
|
bool all_sent = true;
|
2017-08-27 21:55:49 +00:00
|
|
|
|
foreach (Plugins.MetaConversationItem item in items) {
|
|
|
|
|
if (item.mark == Message.Marked.WONTSEND) {
|
|
|
|
|
received_image.visible = true;
|
2017-11-26 18:28:44 +00:00
|
|
|
|
received_image.set_from_icon_name("dialog-warning-symbolic", ICON_SIZE_HEADER);
|
2017-08-27 21:55:49 +00:00
|
|
|
|
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;
|
2017-08-29 22:03:37 +00:00
|
|
|
|
if (item.mark == Message.Marked.UNSENT) {
|
|
|
|
|
all_sent = false;
|
|
|
|
|
}
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (all_read) {
|
|
|
|
|
received_image.visible = true;
|
2017-11-26 18:28:44 +00:00
|
|
|
|
received_image.set_from_icon_name("dino-double-tick-symbolic", ICON_SIZE_HEADER);
|
2017-08-27 21:55:49 +00:00
|
|
|
|
} else if (all_received) {
|
|
|
|
|
received_image.visible = true;
|
2017-11-26 18:28:44 +00:00
|
|
|
|
received_image.set_from_icon_name("dino-tick-symbolic", ICON_SIZE_HEADER);
|
2017-08-29 22:03:37 +00:00
|
|
|
|
} else if (!all_sent) {
|
|
|
|
|
received_image.visible = true;
|
2017-11-26 18:28:44 +00:00
|
|
|
|
received_image.set_from_icon_name("image-loading-symbolic", ICON_SIZE_HEADER);
|
2017-08-27 21:55:49 +00:00
|
|
|
|
} else if (received_image.visible) {
|
2017-11-26 18:28:44 +00:00
|
|
|
|
received_image.set_from_icon_name("image-loading-symbolic", ICON_SIZE_HEADER);
|
|
|
|
|
|
2017-08-27 21:55:49 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-26 18:28:44 +00:00
|
|
|
|
public static string format_time(DateTime datetime, string format_24h, string format_12h) {
|
2017-08-29 19:51:08 +00:00
|
|
|
|
string format = Util.is_24h_format() ? format_24h : format_12h;
|
|
|
|
|
if (!get_charset(null)) {
|
|
|
|
|
// No UTF-8 support, use simple colon for time instead
|
|
|
|
|
format = format.replace("∶", ":");
|
|
|
|
|
}
|
|
|
|
|
return datetime.format(format);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-09 22:31:39 +00:00
|
|
|
|
public static string get_relative_time(DateTime datetime) {
|
2017-08-27 21:55:49 +00:00
|
|
|
|
DateTime now = new DateTime.now_local();
|
|
|
|
|
TimeSpan timespan = now.difference(datetime);
|
|
|
|
|
if (timespan > 365 * TimeSpan.DAY) {
|
2017-08-29 19:51:08 +00:00
|
|
|
|
return format_time(datetime,
|
|
|
|
|
/* xgettext:no-c-format */ /* Date + time in 24h format (w/o seconds) */ _("%x, %H∶%M"),
|
|
|
|
|
/* xgettext:no-c-format */ /* Date + time in 12h format (w/o seconds)*/ _("%x, %l∶%M %p"));
|
2017-08-27 21:55:49 +00:00
|
|
|
|
} else if (timespan > 7 * TimeSpan.DAY) {
|
2017-08-29 19:51:08 +00:00
|
|
|
|
return format_time(datetime,
|
|
|
|
|
/* xgettext:no-c-format */ /* Month, day and time in 24h format (w/o seconds) */ _("%b %d, %H∶%M"),
|
|
|
|
|
/* xgettext:no-c-format */ /* Month, day and time in 12h format (w/o seconds) */ _("%b %d, %l∶%M %p"));
|
2017-08-29 22:03:37 +00:00
|
|
|
|
} else if (datetime.get_day_of_month() != now.get_day_of_month()) {
|
2017-08-29 19:51:08 +00:00
|
|
|
|
return format_time(datetime,
|
|
|
|
|
/* xgettext:no-c-format */ /* Day of week and time in 24h format (w/o seconds) */ _("%a, %H∶%M"),
|
|
|
|
|
/* xgettext:no-c-format */ /* Day of week and time in 12h format (w/o seconds) */_("%a, %l∶%M %p"));
|
2017-08-27 21:55:49 +00:00
|
|
|
|
} else if (timespan > 9 * TimeSpan.MINUTE) {
|
2017-08-29 19:51:08 +00:00
|
|
|
|
return format_time(datetime,
|
|
|
|
|
/* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H∶%M"),
|
|
|
|
|
/* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l∶%M %p"));
|
2017-08-27 21:55:49 +00:00
|
|
|
|
} 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");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|