From 56d2691c314d6a7e05a8eb59a87b2054672bdf6f Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Wed, 28 Oct 2009 15:30:26 -0400 Subject: [PATCH] Reorganize a bit, fix missing-first-message bug https://bugzilla.gnome.org/show_bug.cgi?id=599193 --- js/ui/messaging.js | 138 +++++++++++++++++++++++++++++++++------------ 1 file changed, 102 insertions(+), 36 deletions(-) diff --git a/js/ui/messaging.js b/js/ui/messaging.js index 190d6e6f7..9c4be52eb 100644 --- a/js/ui/messaging.js +++ b/js/ui/messaging.js @@ -1,6 +1,11 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + const DBus = imports.dbus; +const Lang = imports.lang; const Shell = imports.gi.Shell; +const AVATAR_SIZE = 24; + const TELEPATHY = "org.freedesktop.Telepathy."; const CONN = TELEPATHY + "Connection"; const CHANNEL = TELEPATHY + "Channel"; @@ -24,7 +29,10 @@ const ClientObserverIface = { }; const ConnectionIface = { - name: CONN + name: CONN, + signals: [ + { name: 'StatusChanged', inSignature: 'u' } + ] }; const ConnectionAvatarsIface = { @@ -71,6 +79,12 @@ DBus.proxifyPrototype(Channel.prototype, ChannelIface); const ChannelTextIface = { name: CHANNELTEXT, + methods: [ + { name: 'ListPendingMessages', + inSignature: 'b', + outSignature: 'a(uuuuus)' + } + ], signals: [ { name: 'Received', inSignature: 'uuuuus' } ] @@ -107,6 +121,8 @@ Messaging.prototype = { DBus.session.acquire_name(name, DBus.SINGLE_INSTANCE, function(name){log("Acquired name " + name);}, function(name){log("Lost name " + name);}); + + this._conns = {}; }, get Interfaces() { @@ -122,47 +138,97 @@ Messaging.prototype = { ObserveChannels: function(account, conn_path, channels, dispatch_operation, requests_satisfied, observer_info) { - log('observing ' + conn_path); - let conn = new Connection(conn_path); - let conn_name = conn_path.substr(1).replace('/','.','g'); + let conn = this._conns[conn_path]; + if (!conn) { + conn = new Connection(conn_path); + conn.connect('StatusChanged', Lang.bind(this, this._connectionStatusChanged)); + } - for (var i = 0; i < channels.length; i++) { - let path = channels[i][0]; - let props = channels[i][1]; - - let targethandle = props[CHANNEL + '.TargetHandle']; - let targetid = props[CHANNEL + '.TargetID']; - - // conn.RequestAvatarRemote(targethandle, - // function(result, excp) { - // log("called for " + targetid); - // let avatar; - // if (result) { - // let bytes = result[0]; - // avatar = Shell.TextureCache.get_default().load_from_data(bytes, bytes.length, -1, TRAY_HEIGHT); - // } else { - // // fallback avatar - // avatar = Shell.TextureCache.get_default().load_icon_name("stock_person", TRAY_HEIGHT); - // } - // }); - - let channel = new Channel(conn_name, path); - let id = channel.connect('Closed', - function(emitter) { - log('closed'); - channel.disconnect(id); - }); - - let text = new ChannelText(conn_name, path); - text.connect('Received', - function(chan, id, timestamp, sender, type, flags, text) { - log('Received: id ' + id + ', time ' + timestamp + ', sender ' + sender + ', type ' + type + ', flags ' + flags + ': ' + text); - }); + let conn_name = nameify(conn_path); + for (let i = 0; i < channels.length; i++) { + new Source(conn, conn_name, channels[i][0], channels[i][1]); } return [true]; + }, + + _connectionStatusChanged: function(connection, status) { + if (status == Connection_Status.Disconnected) { + delete this._conns[connection.getPath()]; + } } }; DBus.conformExport(Messaging.prototype, ClientIface); DBus.conformExport(Messaging.prototype, ClientObserverIface); + +function Source(conn, conn_name, channel_path, channel_props) { + this._init(conn, conn_name, channel_path, channel_props); +} + +Source.prototype = { + _init: function(conn, conn_name, channel_path, channel_props) { + this._targetId = channel_props[CHANNEL + '.TargetID']; + + log('channel for ' + this._targetId); + + this._pendingMessages = null; + + // FIXME: RequestAvatar is deprecated in favor of + // RequestAvatars; but RequestAvatars provides no explicit + // indication of "no avatar available", so there's no way we + // can reliably wait for it to finish before displaying a + // message. So we use RequestAvatar() instead. + let targethandle = channel_props[CHANNEL + '.TargetHandle']; + conn.RequestAvatarRemote(targethandle, Lang.bind(this, this._gotAvatar)); + + this._channel = new Channel(conn_name, channel_path); + this._closedId = this._channel.connect('Closed', Lang.bind(this, this._channelClosed)); + + this._channelText = new ChannelText(conn_name, channel_path); + this._receivedId = this._channelText.connect('Received', Lang.bind(this, this._receivedMessage)); + + this._channelText.ListPendingMessagesRemote(false, + Lang.bind(this, function(msgs, excp) { + if (msgs) { + log('got pending messages for ' + this._targetId); + this._pendingMessages = msgs; + this._processPendingMessages(); + } + })); + }, + + _gotAvatar: function(result, excp) { + if (result) { + let bytes = result[0]; + this._avatar = Shell.TextureCache.get_default().load_from_data(bytes, bytes.length, AVATAR_SIZE, AVATAR_SIZE); + log('got avatar for ' + this._targetId); + } else { + // fallback avatar (FIXME) + this._avatar = Shell.TextureCache.get_default().load_icon_name("stock_person", AVATAR_SIZE); + log('using default avatar for ' + this._targetId); + } + + this._processPendingMessages(); + }, + + _processPendingMessages: function() { + if (!this._avatar || !this._pendingMessages) + return; + + for (let i = 0; i < this._pendingMessages.length; i++) + this._receivedMessage.apply(this, [this._channel].concat(this._pendingMessages[i])); + this._pendingMessages = null; + }, + + _channelClosed: function() { + log('closed'); + this._channel.disconnect(this._closedId); + this._channelText.disconnect(this._receivedId); + }, + + _receivedMessage: function(channel, id, timestamp, sender, + type, flags, text) { + log('Received: id ' + id + ', time ' + timestamp + ', sender ' + sender + ', type ' + type + ', flags ' + flags + ': ' + text); + } +};