From 926ddc2bdf7de460adaef23fdb7b4a5afedb875c Mon Sep 17 00:00:00 2001 From: Giovanni Campagna Date: Thu, 25 Nov 2010 15:29:41 +0100 Subject: [PATCH] Show timestamp in expanded chat When the last message is older than SCROLLBACK_IMMEDIATE_TIME (1 minutes), show a timestamp in the middle, indicating the time it was sent. Use the same style for presence changes, but show them on the left. https://bugzilla.gnome.org/show_bug.cgi?id=617228 --- data/theme/gnome-shell.css | 7 ++++ js/ui/messageTray.js | 9 +++-- js/ui/telepathyClient.js | 75 ++++++++++++++++++++++++++++++-------- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index c5b6b54c0..58aa1febb 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -957,6 +957,13 @@ StTooltip StLabel { border-radius: 4px; } +.chat-meta-message { + padding-left: 4px; + border-radius: 4px; + font-size: 14px; + color: #bbbbbb; +} + .chat-response { padding: 4px; border-radius: 4px; diff --git a/js/ui/messageTray.js b/js/ui/messageTray.js index 05335b550..e2a4c182d 100644 --- a/js/ui/messageTray.js +++ b/js/ui/messageTray.js @@ -365,7 +365,7 @@ Notification.prototype = { // @actor: actor to add to the body of the notification // // Appends @actor to the notification's body - addActor: function(actor) { + addActor: function(actor, style) { if (!this._scrollArea) { this.actor.add_style_class_name('multi-line-notification'); this._scrollArea = new St.ScrollView({ name: 'notification-scrollview', @@ -382,21 +382,22 @@ Notification.prototype = { this._addBannerBody(); } - this._contentArea.add(actor); + this._contentArea.add(actor, style ? style : {}); this._updated(); }, // addBody: // @text: the text // @markup: %true if @text contains pango markup + // @style: style to use when adding the actor containing the text // // Adds a multi-line label containing @text to the notification. // // Return value: the newly-added label - addBody: function(text, markup) { + addBody: function(text, markup, style) { let label = new URLHighlighter(text, true, markup); - this.addActor(label.actor); + this.addActor(label.actor, style); return label.actor; }, diff --git a/js/ui/telepathyClient.js b/js/ui/telepathyClient.js index 867c63aff..c11d0ba58 100644 --- a/js/ui/telepathyClient.js +++ b/js/ui/telepathyClient.js @@ -3,6 +3,7 @@ const DBus = imports.dbus; const GLib = imports.gi.GLib; const Lang = imports.lang; +const Mainloop = imports.mainloop; const Signals = imports.signals; const St = imports.gi.St; const Gettext = imports.gettext.domain('gnome-shell'); @@ -16,6 +17,7 @@ let contactManager; let channelDispatcher; // See Notification.appendMessage +const SCROLLBACK_IMMEDIATE_TIME = 60; // 1 minute const SCROLLBACK_RECENT_TIME = 15 * 60; // 15 minutes const SCROLLBACK_RECENT_LENGTH = 20; const SCROLLBACK_IDLE_LENGTH = 5; @@ -531,7 +533,7 @@ Source.prototype = { _messageReceived: function(channel, id, timestamp, sender, type, flags, text) { this._ensureNotification(); - this._notification.appendMessage(text); + this._notification.appendMessage(text, timestamp); this.notify(this._notification); }, @@ -565,7 +567,7 @@ Source.prototype = { msg += ' (' + GLib.markup_escape_text(message, -1) + ')'; this._ensureNotification(); - this._notification.appendMessage(msg, true); + this._notification.appendPresence(msg, notify); if (notify) this.notify(this._notification); } @@ -586,23 +588,40 @@ Notification.prototype = { this.setActionArea(this._responseEntry); this._history = []; + this._timestampTimeoutId = 0; }, - appendMessage: function(text, asTitle) { - if (asTitle) - this.update(text, null, { customContent: true }); - else - this.update(this.source.title, text, { customContent: true }); - this._append(text, 'chat-received'); + appendMessage: function(text, timestamp) { + this.update(this.source.title, text, { customContent: true }); + this._append(text, 'chat-received', timestamp); }, - _append: function(text, style) { + _append: function(text, style, timestamp) { + let currentTime = (Date.now() / 1000); + if (!timestamp) + timestamp = currentTime; + let lastMessageTime = -1; + if (this._history.length > 0) + lastMessageTime = this._history[0].time; + + // Reset the old message timeout + if (this._timestampTimeoutId) + Mainloop.source_remove(this._timestampTimeoutId); + let body = this.addBody(text); body.add_style_class_name(style); this.scrollTo(St.Side.BOTTOM); - let now = new Date().getTime() / 1000; - this._history.unshift({ actor: body, time: now }); + this._history.unshift({ actor: body, time: timestamp, realMessage: true }); + + if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) + this._appendTimestamp(); + else + // Schedule a new timestamp in SCROLLBACK_IMMEDIATE_TIME + // from the timestamp of the message. + this._timestampTimeoutId = Mainloop.timeout_add_seconds( + SCROLLBACK_IMMEDIATE_TIME - (currentTime - timestamp), + Lang.bind(this, this._appendTimestamp)); if (this._history.length > 1) { // Keep the scrollback from growing too long. If the most @@ -611,17 +630,43 @@ Notification.prototype = { // SCROLLBACK_RECENT_LENGTH previous messages. Otherwise // we'll keep SCROLLBACK_IDLE_LENGTH messages. - let lastMessageTime = this._history[1].time; - let maxLength = (lastMessageTime < now - SCROLLBACK_RECENT_TIME) ? + let maxLength = (lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME) ? SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH; - if (this._history.length > maxLength) { - let expired = this._history.splice(maxLength); + let filteredHistory = this._history.filter(function(item) { return item.realMessage }); + if (filteredHistory.length > maxLength) { + let lastMessageToKeep = filteredHistory[maxLength]; + let expired = this._history.splice(this._history.indexOf(lastMessageToKeep)); for (let i = 0; i < expired.length; i++) expired[i].actor.destroy(); } } }, + _appendTimestamp: function() { + let lastMessageTime = this._history[0].time; + let lastMessageDate = new Date(lastMessageTime * 1000); + + /* Translators: this is a time format string followed by a date. + If applicable, replace %X with a strftime format valid for your + locale, without seconds. */ + let timeLabel = this.addBody(lastMessageDate.toLocaleFormat(_("Sent at %X on %A")), false, { expand: true, x_fill: false, x_align: St.Align.END }); + timeLabel.add_style_class_name('chat-meta-message'); + this._history.unshift({ actor: timeLabel, time: lastMessageTime, realMessage: false }); + + this._timestampTimeoutId = 0; + return false; + }, + + appendPresence: function(text, asTitle) { + if (asTitle) + this.update(text, null, { customContent: true }); + else + this.update(this.source.title, null, { customContent: true }); + let label = this.addBody(text); + label.add_style_class_name('chat-meta-message'); + this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false}); + }, + grabFocus: function(lockTray) { // Need to call the base class function first so that // it saves where the key focus was before.