[polari/user-list-update: 4/5] userList: Add some useful context to user entries
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [polari/user-list-update: 4/5] userList: Add some useful context to user entries
- Date: Wed, 9 Oct 2013 16:12:10 +0000 (UTC)
commit ea5976c6674382aef78290c73a8db85b51e06823
Author: Florian Müllner <fmuellner gnome org>
Date: Wed Oct 2 16:11:27 2013 +0200
userList: Add some useful context to user entries
Currently the user list is purely informational by indicating who is
present in a channel; make it more useful by displaying some basic
/whois information when selecting a user and offering a context action
to open a private conversation.
data/resources/application.css | 14 +++
src/application.js | 1 +
src/userList.js | 199 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 208 insertions(+), 6 deletions(-)
---
diff --git a/data/resources/application.css b/data/resources/application.css
index 2f25b16..e07c51d 100644
--- a/data/resources/application.css
+++ b/data/resources/application.css
@@ -24,6 +24,20 @@
border-width: 0 0 1px 0;
}
+.polari-user-list .list-row {
+ -GtkWidget-focus-padding: 0;
+}
+
+.polari-user-list .list-row.expanded {
+ background-color: @content_view_bg;
+}
+
+.polari-user-list .list-row GtkFrame {
+ box-shadow: inset 0 2px 4px alpha(@polari_dark_bg_color, 0.8);
+ transition: all 250ms ease-out;
+ border-width: 0;
+}
+
.polari-selection-toolbar,
.polari-input-area {
background-color: @polari_dark_bg_color;
diff --git a/src/application.js b/src/application.js
index db959d9..16931e1 100644
--- a/src/application.js
+++ b/src/application.js
@@ -42,6 +42,7 @@ const Application = new Lang.Class({
window._ = Gettext.gettext;
window.C_ = Gettext.pgettext;
+ window.ngettext = Gettext.ngettext;
Gtk.init(null);
diff --git a/src/userList.js b/src/userList.js
index 085ec08..51e43bc 100644
--- a/src/userList.js
+++ b/src/userList.js
@@ -1,4 +1,6 @@
const Gdk = imports.gi.Gdk;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const Tp = imports.gi.TelepathyGLib;
@@ -112,17 +114,187 @@ const UserListRow = new Lang.Class({
this._createWidget(user);
this.widget.user = user;
+
+ this.widget.connect('unmap', Lang.bind(this, function() {
+ this._revealer.reveal_child = false;
+ }));
+ this.widget.connect('state-flags-changed',
+ Lang.bind(this, this._updateArrowVisibility));
+
+ this._revealer.connect('notify::reveal-child',
+ Lang.bind(this, this._onExpandedChanged));
+ },
+
+ get expand() {
+ return this._revealer.reveal_child;
+ },
+
+ set expand(expand) {
+ this._revealer.reveal_child = expand;
},
_createWidget: function(user) {
this.widget = new Gtk.ListBoxRow();
- let box = new Gtk.Box({ margin: 4, spacing: 4 });
- box.add(new Gtk.Image({ icon_name: 'avatar-default-symbolic' }));
- box.add(new Gtk.Label({ label: user.alias,
- halign: Gtk.Align.START,
- ellipsize: Pango.EllipsizeMode.END }));
- this.widget.add(box);
+
+ let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
+ this.widget.add(vbox);
+
+ let hbox = new Gtk.Box({ margin: 4, spacing: 4 });
+ this._arrow = new Gtk.Arrow({ arrow_type: Gtk.ArrowType.RIGHT,
+ no_show_all: true });
+ hbox.add(new Gtk.Image({ icon_name: 'avatar-default-symbolic' }));
+ hbox.add(new Gtk.Label({ label: user.alias,
+ halign: Gtk.Align.START,
+ hexpand: true,
+ ellipsize: Pango.EllipsizeMode.END }));
+ hbox.add(this._arrow);
+ vbox.add(hbox);
+
+ this._revealer = new Gtk.Revealer({ reveal_child: false });
+ vbox.add(this._revealer);
+
+ let frame = new Gtk.Frame({ hexpand: true });
+ this._revealer.add(frame);
+
+ let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
+ spacing: 6, margin: 6 });
+ frame.add(box);
+
+ this._spinnerBox = new Gtk.Box({ spacing: 6, margin: 12,
+ hexpand: true,
+ halign: Gtk.Align.CENTER });
+ this._spinner = new Gtk.Spinner();
+ this._spinnerBox.add(this._spinner);
+ this._spinnerBox.add(new Gtk.Label({ label: _("Loading details") }));
+ box.add(this._spinnerBox);
+
+ this._detailsGrid = new Gtk.Grid({ row_spacing: 6, column_spacing: 6,
+ hexpand: true });
+ box.add(this._detailsGrid);
+
+ if (user != user.connection.self_contact) {
+ let button = new Gtk.Button({ label: _("Message"),
+ margin_top: 12,
+ hexpand: true,
+ halign: Gtk.Align.END });
+ button.connect('clicked', Lang.bind(this, this._onButtonClicked));
+ user.connection.connect('notify::self-contact', function() {
+ if (user == user.connection.self_contact)
+ button.destroy();
+ });
+ box.add(button);
+ }
+
this.widget.show_all();
+ },
+
+ _formatLast: function(seconds) {
+ if (seconds < 60)
+ return ngettext("%d second ago",
+ "%d seconds ago", seconds).format(seconds);
+
+ let minutes = seconds / 60;
+ if (minutes < 60)
+ return ngettext("%d minute ago",
+ "%d minutes ago", minutes).format(minutes);
+
+ let hours = minutes / 60;
+ if (hours < 24)
+ return ngettext("%d hour ago",
+ "%d hours ago", hours).format(hours);
+
+ let days = hours / 24;
+ if (days < 7)
+ return ngettext("%d day ago",
+ "%d days ago", days).format(days);
+
+ let weeks = days / 7;
+ if (days < 30)
+ return ngettext("%d week ago",
+ "%d weeks ago", weeks).format(weeks);
+
+ let months = days / 30;
+ return ngettext("%d month ago",
+ "%d months ago", months).format(months);
+ },
+
+ _onContactInfoReady: function(c, res) {
+ let fn, last;
+ let info = this.widget.user.get_contact_info();
+ for (let i = 0; i < info.length; i++) {
+ if (info[i].field_name == 'fn')
+ fn = info[i].field_value[0];
+ else if (info[i].field_name == 'x-idle-time')
+ last = info[i].field_value[0];
+ }
+
+ if (!fn)
+ fn = this.widget.user.alias;
+
+ let row = 0;
+ let w = new Gtk.Label({ label: fn, ellipsize: Pango.EllipsizeMode.END,
+ halign: Gtk.Align.START });
+ this._detailsGrid.attach(w, 0, row++, 2, 1);
+
+ if (last) {
+ w = new Gtk.Label({ label: _("Last Activity:"),
+ valign: Gtk.Align.START });
+ this._detailsGrid.attach(w, 0, row, 1, 1);
+
+ w = new Gtk.Label({ label: this._formatLast(last), wrap: true,
+ hexpand: true });
+ this._detailsGrid.attach(w, 1, row++, 1, 1);
+ }
+
+ this._detailsGrid.show_all();
+
+ this._spinner.stop();
+ this._spinnerBox.hide();
+ },
+
+ _onButtonClicked: function() {
+ let account = this.widget.user.connection.get_account();
+
+ let app = Gio.Application.get_default();
+ let action = app.lookup_action('message-user');
+ let time = Gtk.get_current_event().get_time();
+ action.activate(GLib.Variant.new('(ssu)',
+ [ account.get_object_path(),
+ this.widget.user.alias,
+ time ]));
+ },
+
+ _updateArrowVisibility: function() {
+ let flags = this.widget.get_state_flags();
+ this._arrow.visible = this.expand ||
+ flags & Gtk.StateFlags.PRELIGHT ||
+ flags & Gtk.StateFlags.FOCUSED;
+ },
+
+ _onExpandedChanged: function() {
+ if (this._revealer.reveal_child) {
+ this.widget.get_style_context().add_class('expanded');
+ this._arrow.arrow_type = Gtk.ArrowType.DOWN;
+
+ this._spinnerBox.show();
+ this._spinner.start();
+
+ this._detailsGrid.foreach(function(w) { w.destroy(); });
+
+ this._cancellable = new Gio.Cancellable();
+ this.widget.user.request_contact_info_async(this._cancellable,
+ Lang.bind(this, this._onContactInfoReady));
+ } else {
+ this.widget.get_style_context().remove_class('expanded');
+ this._arrow.arrow_type = Gtk.ArrowType.RIGHT;
+ this._updateArrowVisibility();
+
+ this._spinner.stop();
+
+ if (this._cancellable)
+ this._cancellable.cancel();
+ this._cancellable = null;
+ }
}
});
@@ -141,8 +313,12 @@ const UserList = new Lang.Class({
this._list.set_filter_func(Lang.bind(this, this._filterRows));
this._list.set_sort_func(Lang.bind(this, this._sort));
+ this._list.connect('row-activated',
+ Lang.bind(this, this._onRowActivated));
+
this._room = room;
this._rows = {};
+ this._activeRow = null;
room.connect('member-renamed',
Lang.bind(this, this._onMemberRenamed));
@@ -195,6 +371,17 @@ const UserList = new Lang.Class({
delete this._rows[member];
},
+ _setActiveRow: function(row) {
+ if (this._activeRow && this._activeRow != row)
+ this._activeRow.expand = false;
+ this._activeRow = row;
+ },
+
+ _onRowActivated: function(list, row) {
+ this._setActiveRow(this._rows[row.user]);
+ this._activeRow.expand = !this._activeRow.expand;
+ },
+
_sort: function(row1, row2) {
return row1.user.alias.localeCompare(row2.user.alias);
},
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]