Show last message sender in conversation list

This commit is contained in:
fiaxh 2017-09-01 12:42:14 +02:00
parent dc26841b9e
commit addd5a013f
6 changed files with 168 additions and 222 deletions

View file

@ -9,11 +9,10 @@
<property name="reveal-child">False</property>
<property name="visible">True</property>
<child>
<object class="GtkGrid">
<object class="GtkBox">
<property name="orientation">horizontal</property>
<property name="margin">7</property>
<property name="margin-right">14</property>
<property name="column-spacing">10</property>
<property name="visible">True</property>
<child>
<object class="GtkImage" id="image">
@ -23,114 +22,117 @@
</object>
</child>
<child>
<object class="GtkGrid">
<object class="GtkBox">
<property name="margin-left">10</property>
<property name="orientation">vertical</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="name_label">
<property name="max_width_chars">1</property>
<property name="ellipsize">end</property>
<property name="expand">True</property>
<property name="margin-right">7</property>
<property name="xalign">0</property>
<property name="visible">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkRevealer" id="time_revealer">
<property name="transition-type">slide-right</property>
<property name="transition-duration">100</property>
<property name="reveal-child">True</property>
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="time_label">
<property name="hexpand">False</property>
<property name="xalign">1</property>
<attributes>
<attribute name="scale" value="0.7"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="message_label">
<property name="max_width_chars">1</property>
<property name="ellipsize">end</property>
<property name="expand">True</property>
<property name="xalign">0</property>
<attributes>
<attribute name="scale" value="0.8"/>
</attributes>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
<property name="width">2</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="visible">True</property>
<property name="orientation">horizontal</property>
<child>
<object class="GtkBox">
<property name="vexpand">True</property>
<object class="GtkLabel" id="name_label">
<property name="max_width_chars">1</property>
<property name="ellipsize">end</property>
<property name="expand">True</property>
<property name="margin-right">7</property>
<property name="xalign">0</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkRevealer" id="xbutton_revealer">
<property name="transition-type">slide-left</property>
<object class="GtkRevealer" id="time_revealer">
<property name="transition-type">slide-right</property>
<property name="transition-duration">100</property>
<property name="reveal-child">False</property>
<property name="reveal-child">True</property>
<property name="visible">True</property>
<child>
<object class="GtkButton" id="x_button">
<property name="vexpand">False</property>
<property name="visible">True</property>
<style>
<class name="conversation_list_row_xbutton"/>
<class name="circular"/>
<class name="flat"/>
</style>
<child>
<object class="GtkImage">
<property name="icon-name">window-close-symbolic</property>
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
<object class="GtkLabel" id="time_label">
<property name="hexpand">False</property>
<property name="xalign">1</property>
<attributes>
<attribute name="scale" value="0.7"/>
</attributes>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="orientation">horizontal</property>
<child>
<object class="GtkBox">
<property name="vexpand">True</property>
<property name="visible">True</property>
<object class="GtkLabel" id="nick_label">
<property name="max_width_chars">15</property>
<property name="ellipsize">end</property>
<property name="xalign">0</property>
<style>
<class name="dim-label"/>
</style>
<attributes>
<!--<attribute name="weight" value="bold"/>-->
<attribute name="scale" value="0.8"/>
</attributes>
</object>
</child>
<child>
<object class="GtkLabel" id="message_label">
<property name="max_width_chars">1</property>
<property name="ellipsize">end</property>
<property name="expand">True</property>
<property name="xalign">0</property>
<attributes>
<attribute name="scale" value="0.8"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
<property name="width">1</property>
<property name="height">2</property>
</packing>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="visible">True</property>
<child>
<object class="GtkBox">
<property name="vexpand">True</property>
<property name="visible">True</property>
</object>
</child>
<child>
<object class="GtkRevealer" id="xbutton_revealer">
<property name="transition-type">slide-left</property>
<property name="transition-duration">100</property>
<property name="reveal-child">False</property>
<property name="visible">True</property>
<child>
<object class="GtkButton" id="x_button">
<property name="vexpand">False</property>
<property name="visible">True</property>
<style>
<class name="conversation_list_row_xbutton"/>
<class name="circular"/>
<class name="flat"/>
</style>
<child>
<object class="GtkImage">
<property name="icon-name">window-close-symbolic</property>
<property name="icon-size">1</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<property name="vexpand">True</property>
<property name="visible">True</property>
</object>
</child>
</object>
</child>

View file

@ -2,7 +2,6 @@ using Gdk;
using Gee;
using Gtk;
using Xmpp;
using Dino.Entities;
namespace Dino.Ui.ConversationSelector {
@ -16,36 +15,24 @@ public class ChatRow : ConversationRow {
tooltip.set_custom(generate_tooltip());
return true;
});
update_avatar();
stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => {
if (conversation.account.equals(account) && conversation.counterpart.equals(jid)) {
update_name_label();
update_avatar();
}
});
}
public override void on_show_received(Show show) {
update_avatar();
}
public override void network_connection(bool connected) {
if (!connected) {
set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor)).set_greyscale(true).draw_conversation(stream_interactor, conversation), image.scale_factor);
protected override void update_message_label() {
base.update_message_label();
if (last_message != null && last_message.direction == Message.DIRECTION_SENT) {
nick_label.visible = true;
nick_label.label = _("Me") + ": ";
} else {
update_avatar();
nick_label.label = "";
}
}
public void on_updated_roster_item(Roster.Item roster_item) {
if (roster_item.name != null) {
display_name = roster_item.name;
update_name();
}
update_avatar();
}
public void update_avatar() {
ArrayList<Jid> full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(conversation.counterpart, conversation.account);
set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor))
.set_greyscale(full_jids == null)
.draw_conversation(stream_interactor, conversation), image.scale_factor);
}
private Widget generate_tooltip() {
Builder builder = new Builder.from_resource("/im/dino/conversation_selector/chat_row_tooltip.ui");
Box main_box = builder.get_object("main_box") as Box;

View file

@ -3,7 +3,6 @@ using Gdk;
using Gtk;
using Pango;
using Xmpp;
using Dino.Entities;
namespace Dino.Ui.ConversationSelector {
@ -14,21 +13,20 @@ public abstract class ConversationRow : ListBoxRow {
public signal void closed();
[GtkChild] protected Image image;
[GtkChild] private Label name_label;
[GtkChild] private Label time_label;
[GtkChild] private Label message_label;
[GtkChild] protected Label name_label;
[GtkChild] protected Label time_label;
[GtkChild] protected Label nick_label;
[GtkChild] protected Label message_label;
[GtkChild] protected Button x_button;
[GtkChild] private Revealer time_revealer;
[GtkChild] private Revealer xbutton_revealer;
[GtkChild] protected Revealer time_revealer;
[GtkChild] protected Revealer xbutton_revealer;
[GtkChild] public Revealer main_revealer;
public Conversation conversation { get; private set; }
protected const int AVATAR_SIZE = 40;
protected string display_name;
protected string message;
protected DateTime time;
protected Message? last_message;
protected bool read = true;
@ -42,27 +40,23 @@ public abstract class ConversationRow : ListBoxRow {
this.conversation = conversation;
this.stream_interactor = stream_interactor;
x_button.clicked.connect(on_x_button_clicked);
x_button.clicked.connect(close_conversation);
stream_interactor.connection_manager.connection_state_changed.connect(update_avatar);
update_name(Util.get_conversation_display_name(stream_interactor, conversation));
update_name_label();
update_avatar();
message_received();
}
public void update() {
update_time();
update_time_label();
}
public void message_received(Entities.Message? m = null) {
Entities.Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message(conversation);
if (message != null) {
update_message(message.body.replace("\n", " "));
update_time(message.time);
}
}
public void set_avatar(Pixbuf pixbuf, int scale_factor = 1) {
Util.image_set_from_scaled_pixbuf(image, pixbuf, scale_factor);
image.queue_draw();
last_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message(conversation) ?? m;
update_message_label();
update_time_label();
}
public void mark_read() {
@ -73,33 +67,39 @@ public abstract class ConversationRow : ListBoxRow {
update_read(false);
}
public abstract void on_show_received(Show presence);
public abstract void network_connection(bool connected);
protected void update_name(string? new_name = null) {
if (new_name != null) {
display_name = new_name;
}
name_label.label = display_name;
public virtual void on_show_received(Show presence) {
update_avatar();
}
protected void update_time(DateTime? new_time = null) {
time_label.visible = true;
if (new_time != null) {
time = new_time;
}
if (time != null) {
time_label.label = get_relative_time(time.to_local());
public void update_avatar() {
bool self_online = stream_interactor.connection_manager.get_state(conversation.account) == ConnectionManager.ConnectionState.CONNECTED;
bool counterpart_online = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(conversation.counterpart, conversation.account) != null;
bool greyscale = !self_online || !counterpart_online;
Idle.add(() => {
Pixbuf pixbuf = ((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor))
.set_greyscale(greyscale)
.draw_conversation(stream_interactor, conversation));
Util.image_set_from_scaled_pixbuf(image, pixbuf, image.get_scale_factor());
return false;
});
}
protected void update_name_label(string? new_name = null) {
name_label.label = Util.get_conversation_display_name(stream_interactor, conversation);
}
protected void update_time_label(DateTime? new_time = null) {
if (last_message != null) {
time_label.visible = true;
time_label.label = get_relative_time(last_message.time.to_local());
}
}
protected void update_message(string? new_message = null) {
if (new_message != null) {
message = new_message;
}
if (message != null) {
protected virtual void update_message_label() {
if (last_message != null) {
message_label.visible = true;
message_label.label = message;
message_label.label = last_message.body.replace("\n", " ");
}
}
@ -108,14 +108,17 @@ public abstract class ConversationRow : ListBoxRow {
if (read) {
name_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD)));
time_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD)));
nick_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD)));
message_label.attributes.filter((attr) => attr.equal(attr_weight_new(Weight.BOLD)));
} else {
name_label.attributes.insert(attr_weight_new(Weight.BOLD));
time_label.attributes.insert(attr_weight_new(Weight.BOLD));
nick_label.attributes.insert(attr_weight_new(Weight.BOLD));
message_label.attributes.insert(attr_weight_new(Weight.BOLD));
}
name_label.label = name_label.label; // TODO initializes redrawing, which would otherwise not happen. nicer?
time_label.label = time_label.label;
nick_label.label = nick_label.label;
message_label.label = message_label.label;
}
@ -142,7 +145,7 @@ public abstract class ConversationRow : ListBoxRow {
return box;
}
private void on_x_button_clicked() {
private void close_conversation() {
main_revealer.set_transition_type(RevealerTransitionType.SLIDE_UP);
main_revealer.set_reveal_child(false);
closed();
@ -177,8 +180,8 @@ public abstract class ConversationRow : ListBoxRow {
return _("Yesterday");
} 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"));
/* 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"));
} else if (timespan > 1 * TimeSpan.MINUTE) {
ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE);
return n("%i min ago", "%i mins ago", mins).printf(mins);

View file

@ -2,7 +2,6 @@ using Gdk;
using Gee;
using Gtk;
using Xmpp;
using Dino.Entities;
namespace Dino.Ui.ConversationSelector {
@ -19,25 +18,16 @@ public class GroupchatPmRow : ConversationRow {
update_avatar();
}
public override void on_show_received(Show show) {
update_avatar();
}
public override void network_connection(bool connected) {
if (!connected) {
set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor)).set_greyscale(true).draw_conversation(stream_interactor, conversation), image.scale_factor);
protected override void update_message_label() {
base.update_message_label();
if (last_message != null && last_message.direction == Message.DIRECTION_SENT) {
nick_label.visible = true;
nick_label.label = _("Me") + ": ";
} else {
update_avatar();
nick_label.label = "";
}
}
public void update_avatar() {
ArrayList<Jid> full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(conversation.counterpart, conversation.account);
set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor))
.set_greyscale(full_jids == null)
.draw_conversation(stream_interactor, conversation), image.scale_factor);
}
private Widget generate_tooltip() {
Builder builder = new Builder.from_resource("/im/dino/conversation_selector/chat_row_tooltip.ui");
Box main_box = builder.get_object("main_box") as Box;

View file

@ -10,36 +10,18 @@ public class GroupchatRow : ConversationRow {
set_tooltip_text(conversation.counterpart.bare_jid.to_string());
update_avatar();
x_button.clicked.connect(on_x_button_clicked);
stream_interactor.get_module(MucManager.IDENTITY).left.connect(() => {
Idle.add(() => {
update_avatar();
return false;
});
closed.connect(() => {
stream_interactor.get_module(MucManager.IDENTITY).part(conversation.account, conversation.counterpart);
});
stream_interactor.get_module(MucManager.IDENTITY).left.connect(update_avatar);
}
public override void on_show_received(Show show) {
set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor))
.draw_conversation(stream_interactor, conversation), image.scale_factor);
}
public override void network_connection(bool connected) {
update_avatar();
}
private void on_x_button_clicked() {
stream_interactor.get_module(MucManager.IDENTITY).part(conversation.account, conversation.counterpart);
}
private void update_avatar() {
ConnectionManager.ConnectionState connection_state = stream_interactor.connection_manager.get_state(conversation.account);
bool is_joined = stream_interactor.get_module(MucManager.IDENTITY).is_joined(conversation.counterpart, conversation.account);
set_avatar((new AvatarGenerator(AVATAR_SIZE, AVATAR_SIZE, image.scale_factor))
.set_greyscale(connection_state != ConnectionManager.ConnectionState.CONNECTED || !is_joined)
.draw_conversation(stream_interactor, conversation), image.scale_factor);
protected override void update_message_label() {
base.update_message_label();
if (last_message != null) {
nick_label.visible = true;
nick_label.label = Util.get_message_display_name(stream_interactor, last_message, conversation.account) + ": ";
}
}
}

View file

@ -48,16 +48,6 @@ public class List : ListBox {
return false;
});
});
stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => {
Idle.add(() => {
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account);
if (conversation != null && rows.has_key(conversation)) {
ChatRow row = rows[conversation] as ChatRow;
if (row != null) row.on_updated_roster_item(roster_item);
}
return false;
});
});
stream_interactor.get_module(AvatarManager.IDENTITY).received_avatar.connect((avatar, jid, account) => {
Idle.add(() => {
Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account);
@ -68,14 +58,6 @@ public class List : ListBox {
return false;
});
});
stream_interactor.connection_manager.connection_state_changed.connect((account, state) => {
Idle.add(() => {
foreach (ConversationRow row in rows.values) {
if (row.conversation.account.equals(account)) row.network_connection(state == ConnectionManager.ConnectionState.CONNECTED);
}
return false;
});
});
Timeout.add_seconds(60, () => {
foreach (ConversationRow row in rows.values) row.update();
return true;