[gnome-shell/message-tray] [messaging] update avatar code to use non-deprecated interfaces



commit 6c3b8e2add8d278b160cb3f5555ef331ef6ffe64
Author: Dan Winship <danw gnome org>
Date:   Tue Dec 8 09:54:28 2009 -0500

    [messaging] update avatar code to use non-deprecated interfaces
    
    Also fixes it to notice avatar updates
    
    https://bugzilla.gnome.org/show_bug.cgi?id=603546

 js/ui/messaging.js |  166 +++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 119 insertions(+), 47 deletions(-)
---
diff --git a/js/ui/messaging.js b/js/ui/messaging.js
index 2e54cbb..db007d7 100644
--- a/js/ui/messaging.js
+++ b/js/ui/messaging.js
@@ -3,6 +3,7 @@
 const DBus = imports.dbus;
 const Lang = imports.lang;
 const Shell = imports.gi.Shell;
+const St = imports.gi.St;
 
 const Main = imports.ui.main;
 
@@ -61,9 +62,21 @@ DBus.proxifyPrototype(Connection.prototype, ConnectionIface);
 const ConnectionAvatarsIface = {
     name: CONN + '.Interface.Avatars',
     methods: [
-        { name: 'RequestAvatar',
-          inSignature: 'u',
-          outSignature: 'ays'
+        { name: 'GetKnownAvatarTokens',
+          inSignature: 'au',
+          outSignature: 'a{us}'
+        },
+        { name: 'RequestAvatars',
+          inSignature: 'au',
+          outSignature: ''
+        }
+    ],
+    signals: [
+        { name: 'AvatarRetrieved',
+          inSignature: 'usays'
+        },
+        { name: 'AvatarUpdated',
+          inSignature: 'us'
         }
     ]
 };
@@ -75,6 +88,96 @@ function ConnectionAvatars(path) {
 ConnectionAvatars.prototype = {
     _init: function(path) {
         DBus.session.proxifyObject(this, nameify(path), path);
+
+        this.connect('AvatarUpdated', Lang.bind(this, this._avatarUpdated));
+        this.connect('AvatarRetrieved', Lang.bind(this, this._avatarRetrieved));
+
+        // _avatarData[handle] describes the icon for @handle: either
+        // the string 'default', meaning to use the default avatar, or
+        // an array of bytes containing, eg, PNG data.
+        this._avatarData = {};
+
+        // _icons[handle] is an array of the icon actors currently
+        // being displayed for @handle. These will be updated
+        // automatically if @handle's avatar changes.
+        this._icons = {};
+    },
+
+    _avatarUpdated: function(iface, handle, token) {
+        if (!this._avatarData[handle]) {
+            // This would only happen if we get an AvatarUpdated
+            // signal for an avatar while there's already a
+            // RequestAvatars() call pending, so we don't need to do
+            // anything.
+            return;
+        }
+
+        if (token == '') {
+            // Invoke the next async callback in the chain, telling
+            // it to use the default image.
+            this._avatarRetrieved(this, handle, token, 'default', null);
+        } else {
+            // In this case, @token is some sort of UUID. Telepathy
+            // expects us to cache avatar images to disk and use the
+            // tokens to figure out when we already have the right
+            // images cached. But we don't do that, we just
+            // ignore @token and request the image unconditionally.
+            this.RequestAvatarsRemote([handle]);
+        }
+    },
+
+    _createIcon: function(iconData, size) {
+        let textureCache = Shell.TextureCache.get_default();
+        if (iconData == 'default')
+            return textureCache.load_icon_name('stock_person', size);
+        else
+            return textureCache.load_from_data(iconData, iconData.length, size);
+    },
+
+    _avatarRetrieved: function(iface, handle, token, avatarData, mimeType) {
+        this._avatarData[handle] = avatarData;
+        if (!this._icons[handle])
+            return;
+
+        for (let i = 0; i < this._icons[handle].length; i++) {
+            let iconBox = this._icons[handle][i];
+            let size = iconBox.child.height;
+            iconBox.child = this._createIcon(avatarData, size);
+        }
+    },
+
+    createAvatar: function(handle, size) {
+        let iconBox = new St.Bin({ style_class: 'avatar-box' });
+
+        if (!this._icons[handle])
+            this._icons[handle] = [];
+        this._icons[handle].push(iconBox);
+
+        iconBox.connect('destroy', Lang.bind(this,
+            function() {
+                let i = this._icons[handle].indexOf(iconBox);
+                if (i != -1)
+                    this._icons[handle].splice(i, 1);
+            }));
+
+        let avatarData = this._avatarData[handle];
+        if (avatarData) {
+            iconBox.child = this._createIcon(avatarData, size);
+            return iconBox;
+        }
+
+        // Fill in the default icon and then asynchronously load
+        // the real avatar.
+        iconBox.child = this._createIcon('default', size);
+        this.GetKnownAvatarTokensRemote([handle], Lang.bind(this,
+            function (tokens, excp) {
+                if (tokens && tokens[handle])
+                    this.RequestAvatarsRemote([handle]);
+                else
+                    this._avatarData[handle] = 'default';
+            }));
+
+        return iconBox;
     }
 };
 
@@ -288,56 +391,28 @@ Source.prototype = {
         this._channel = new Channel(connName, channelPath);
         this._closedId = this._channel.connect('Closed', Lang.bind(this, this._channelClosed));
 
+        this._targetHandle = targetHandle;
         this._targetId = targetId;
         log('channel for ' + this._targetId + ' channelPath ' + channelPath);
 
-        this._pendingMessages = null;
-
-        this._avatar = null;
-        this._avatarBytes = 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 connAv = new ConnectionAvatars(conn.getPath());
-        connAv.RequestAvatarRemote(targetHandle, Lang.bind(this, this._gotAvatar));
+        this._avatars = new ConnectionAvatars(conn.getPath());
 
         this._channelText = new ChannelText(connName, channelPath);
         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();
-                }
-            }));
+        this._channelText.ListPendingMessagesRemote(false, Lang.bind(this, this._gotPendingMessages));
     },
 
-    _gotAvatar: function(result, excp) {
-        if (result) {
-            this._avatarBytes = result[0];
-            this._avatar = Shell.TextureCache.get_default().load_from_data(this._avatarBytes, this._avatarBytes.length, 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();
+    _createIcon: function() {
+        return this._avatars.createAvatar(this._targetHandle, AVATAR_SIZE);
     },
 
-    _processPendingMessages: function() {
-        if (!this._avatar || !this._pendingMessages)
+    _gotPendingMessages: function(msgs, excp) {
+        if (!msgs)
             return;
 
-        for (let i = 0; i < this._pendingMessages.length; i++)
-            this._receivedMessage.apply(this, [this._channel].concat(this._pendingMessages[i]));
-        this._pendingMessages = null;
+        for (let i = 0; i < msgs.length; i++)
+            this._receivedMessage.apply(this, [this._channel].concat(msgs[i]));
     },
 
     _channelClosed: function() {
@@ -350,14 +425,11 @@ Source.prototype = {
     _receivedMessage: function(channel, id, timestamp, sender,
                                type, flags, text) {
         log('Received: id ' + id + ', time ' + timestamp + ', sender ' + sender + ', type ' + type + ', flags ' + flags + ': ' + text);
-        Main.notificationPopup.show(this._avatar, text);
+        let popupAvatar = this._createIcon();
+        Main.notificationPopup.show(popupAvatar, text);
         if (!Main.messageTray.contains(this._targetId)) {
-            let avatarForMessageTray = null;
-            if (this._avatarBytes)
-                avatarForMessageTray = Shell.TextureCache.get_default().load_from_data(this._avatarBytes, this._avatarBytes.length, AVATAR_SIZE);
-            else
-                avatarForMessageTray = Shell.TextureCache.get_default().load_icon_name("stock_person", AVATAR_SIZE);
-            Main.messageTray.add(this._targetId, avatarForMessageTray);
+            let trayAvatar = this._createIcon();
+            Main.messageTray.add(this._targetId, trayAvatar);
         }
     }
 };



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]