[polari/wip/raresv/nick-popover: 36/47] chatView: Split out status tracking



commit 6c4ab588ca67beb398d889a0bff97d3e0414ded1
Author: raresv <rares visalom gmail com>
Date:   Sun Aug 21 17:14:04 2016 +0300

    chatView: Split out status tracking
    
    Tracking the online status of nicks is not just useful for updating
    colors in the chat log, so move the code to a dedicated module.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=760853

 src/chatView.js                        |  116 +++++++++-------------------
 src/org.gnome.Polari.src.gresource.xml |    1 +
 src/userTracker.js                     |  129 ++++++++++++++++++++++++++++++++
 3 files changed, 168 insertions(+), 78 deletions(-)
---
diff --git a/src/chatView.js b/src/chatView.js
index d8bde62..77118a9 100644
--- a/src/chatView.js
+++ b/src/chatView.js
@@ -14,6 +14,7 @@ const Mainloop = imports.mainloop;
 const PasteManager = imports.pasteManager;
 const Signals = imports.signals;
 const Utils = imports.utils;
+const UserTracker = imports.userTracker;
 
 const MAX_NICK_CHARS = 8;
 const IGNORE_STATUS_TIME = 5;
@@ -294,6 +295,9 @@ const ChatView = new Lang.Class({
         this._initialPending = [];
         this._statusCount = { left: 0, joined: 0, total: 0 };
 
+        this._userTracker = new UserTracker.UserTracker(this._room);
+        this._userTracker.connect('status-changed', Lang.bind(this, this._onNickStatusChanged));
+
         this._room.account.connect('notify::nickname', Lang.bind(this,
             function() {
                 this._updateMaxNickChars(this._room.account.nickname.length);
@@ -386,19 +390,6 @@ const ChatView = new Lang.Class({
         });
     },
 
-    _foreachNickTag: function(func) {
-        let tagTable = this._view.get_buffer().get_tag_table();
-        tagTable.foreach(function(tag) {
-            if (tag._contacts)
-                func(tag);
-        });
-    },
-
-    _resetNickTag: function(nickTag) {
-        nickTag._contacts = [];
-        this._updateTagStatus(nickTag);
-    },
-
     _onStyleUpdated: function() {
         let context = this.get_style_context();
         context.save();
@@ -451,7 +442,17 @@ const ChatView = new Lang.Class({
             }
         });
 
-        this._foreachNickTag(t => { this._updateTagStatus(t); });
+        tagTable.foreach(tag => {
+            if(!tag.name)
+                return;
+
+            let nickname = this._getNickFromTagName(tag.name);
+
+            if (!nickname)
+                return;
+
+            this._updateNickTag(tag, this._userTracker.getNickStatus(nickname));
+        });
     },
 
     _onDestroy: function() {
@@ -554,15 +555,6 @@ const ChatView = new Lang.Class({
 
         if (!this._channel)
             return;
-
-        if (this._room.type == Tp.HandleType.ROOM) {
-            let members = this._channel.group_dup_members_contacts();
-            for (let j = 0; j < members.length; j++)
-                this._trackContact(members[j]);
-        } else {
-                this._trackContact(this._channel.connection.self_contact);
-                this._trackContact(this._channel.target_contact);
-        }
     },
 
     get max_nick_chars() {
@@ -836,38 +828,10 @@ const ChatView = new Lang.Class({
         return NICKTAG_PREFIX + Polari.util_get_basenick(nick);
     },
 
-    _trackContact: function(contact) {
-        let nickTag = this._lookupTag(this._getNickTagName(contact.alias));
-        if (!nickTag)
-           return;
-
-        let alreadyTracked = nickTag._contacts.some(c => c.alias == contact.alias);
-
-        if (!alreadyTracked)
-            nickTag._contacts.push(contact);
-
-        this._updateTagStatus(nickTag);
-    },
-
-    _untrackContact: function(contact) {
-        let nickTag = this._lookupTag(this._getNickTagName(contact.alias));
-        if (!nickTag)
-            return;
-
-        let indexToDelete = nickTag._contacts.map(c => c.alias).indexOf(contact.alias);
-
-        if (indexToDelete > -1) {
-            nickTag._contacts.splice(indexToDelete, 1);
-
-            this._updateTagStatus(nickTag);
-        }
-    },
-
-    _updateTagStatus: function(tag) {
-        if (tag._contacts.length == 0)
-            tag.foreground_rgba = this._inactiveNickColor;
-        else
-            tag.foreground_rgba = this._activeNickColor;
+    _getNickFromTagName: function(tagName) {
+        if (tagName.startsWith(NICKTAG_PREFIX))
+            return tagName.replace(NICKTAG_PREFIX, '');
+        return null;
     },
 
     _onChannelChanged: function() {
@@ -891,19 +855,6 @@ const ChatView = new Lang.Class({
                                  : this._room.account.nickname;
         this._updateMaxNickChars(nick.length);
 
-        if (this._channel) {
-            if (this._room.type == Tp.HandleType.ROOM) {
-                let members = this._channel.group_dup_members_contacts();
-                for (let j = 0; j < members.length; j++)
-                    this._trackContact(members[j]);
-            } else {
-                this._trackContact(this._channel.connection.self_contact);
-                this._trackContact(this._channel.target_contact);
-            }
-        } else {
-            this._foreachNickTag(t => { this._resetNickTag(t); });
-        }
-
         if (!this._channel)
             return;
 
@@ -928,8 +879,6 @@ const ChatView = new Lang.Class({
     _onMemberRenamed: function(room, oldMember, newMember) {
         let text = _("%s is now known as %s").format(oldMember.alias, newMember.alias);
         this._insertStatus(text, oldMember.alias, 'renamed');
-        this._untrackContact(oldMember);
-        this._trackContact(newMember);
     },
 
     _onMemberDisconnected: function(room, member, message) {
@@ -937,7 +886,6 @@ const ChatView = new Lang.Class({
         if (message)
             text += ' (%s)'.format(message);
         this._insertStatus(text, member.alias, 'left');
-        this._untrackContact(member);
     },
 
     _onMemberKicked: function(room, member, actor) {
@@ -946,7 +894,6 @@ const ChatView = new Lang.Class({
                                                          actor.alias)
                   : _("%s has been kicked").format(member.alias);
         this._insertStatus(message, member.alias, 'left');
-        this._untrackContact(member);
     },
 
     _onMemberBanned: function(room, member, actor) {
@@ -955,13 +902,11 @@ const ChatView = new Lang.Class({
                                                          actor.alias)
                   : _("%s has been banned").format(member.alias)
         this._insertStatus(message, member.alias, 'left');
-        this._untrackContact(member);
     },
 
     _onMemberJoined: function(room, member) {
         let text = _("%s joined").format(member.alias);
         this._insertStatus(text, member.alias, 'joined');
-        this._trackContact(member);
     },
 
     _onMemberLeft: function(room, member, message) {
@@ -971,7 +916,6 @@ const ChatView = new Lang.Class({
             text += ' (%s)'.format(message);
 
         this._insertStatus(text, member.alias, 'left');
-        this._untrackContact(member);
     },
 
     _onMessageReceived: function(channel, tpMessage) {
@@ -1194,7 +1138,6 @@ const ChatView = new Lang.Class({
 
         let iter = this._view.buffer.get_end_iter();
         this._insertMessage(iter, message, this._state);
-        this._trackContact(tpMessage.sender);
 
         if (message.pendingId == undefined /* outgoing */ ||
             (this._app.isRoomFocused(this._room) && this._pending.size == 0))
@@ -1237,9 +1180,10 @@ const ChatView = new Lang.Class({
 
                 if (!nickTag) {
                     nickTag = new Gtk.TextTag({ name: nickTagName });
-                    this._view.get_buffer().get_tag_table().add(nickTag);
 
-                    this._resetNickTag(nickTag);
+                    this._updateNickTag(nickTag, this._userTracker.getNickStatus(message.nick));
+
+                    this._view.get_buffer().get_tag_table().add(nickTag);
                 }
                 tags.push(nickTag);
                 if (needsGap)
@@ -1280,6 +1224,22 @@ const ChatView = new Lang.Class({
                               this._view.buffer.create_mark(null, iter, true));
     },
 
+    _onNickStatusChanged: function(tracker, nickName, status) {
+        let nickTag = this._lookupTag(this._getNickTagName(nickName));
+
+        if (!nickTag)
+            return;
+
+        this._updateNickTag(nickTag, status);
+    },
+
+    _updateNickTag: function(tag, status) {
+        if (status == Tp.ConnectionPresenceType.AVAILABLE)
+            tag.foreground_rgba = this._activeNickColor;
+        else
+            tag.foreground_rgba = this._inactiveNickColor;
+    },
+
     _createUrlTag: function(url) {
         if (url.indexOf(':') == -1)
             url = 'http://' + url;
diff --git a/src/org.gnome.Polari.src.gresource.xml b/src/org.gnome.Polari.src.gresource.xml
index 4e3a0ec..17dc187 100644
--- a/src/org.gnome.Polari.src.gresource.xml
+++ b/src/org.gnome.Polari.src.gresource.xml
@@ -20,5 +20,6 @@
     <file>telepathyClient.js</file>
     <file>userList.js</file>
     <file>utils.js</file>
+    <file>userTracker.js</file>
   </gresource>
 </gresources>
diff --git a/src/userTracker.js b/src/userTracker.js
new file mode 100644
index 0000000..57e2937
--- /dev/null
+++ b/src/userTracker.js
@@ -0,0 +1,129 @@
+const Polari = imports.gi.Polari;
+const Lang = imports.lang;
+const Tp = imports.gi.TelepathyGLib;
+const Signals = imports.signals;
+const GObject = imports.gi.GObject;
+const Utils = imports.utils;
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+
+const UserTracker = new Lang.Class({
+    Name: 'UserTracker',
+    Extends: GObject.Object,
+
+    Signals: {
+        'status-changed': {
+            flags: GObject.SignalFlags.DETAILED,
+            param_types: [GObject.TYPE_STRING, GObject.TYPE_INT]
+        }
+    },
+
+    _init: function(room) {
+        this.parent();
+
+        this._baseNickContacts = new Map();
+
+        this._room = room;
+
+        this._onRoomAdded(this._room);
+        this._onChannelChanged(this._room);
+    },
+
+    _onRoomAdded: function(room) {
+        let roomSignals = [
+            { name: 'notify::channel',
+              handler: Lang.bind(this, this._onChannelChanged) },
+            { name: 'member-renamed',
+              handler: Lang.bind(this, this._onMemberRenamed) },
+            { name: 'member-disconnected',
+              handler: Lang.bind(this, this._onMemberLeft) },
+            { name: 'member-kicked',
+              handler: Lang.bind(this, this._onMemberLeft) },
+            { name: 'member-banned',
+              handler: Lang.bind(this, this._onMemberLeft) },
+            { name: 'member-joined',
+              handler: Lang.bind(this, this._onMemberJoined) },
+            { name: 'member-left',
+              handler: Lang.bind(this, this._onMemberLeft) }
+        ];
+
+        roomSignals.forEach(Lang.bind(this, function(signal) {
+            room.connect(signal.name, signal.handler);
+        }));
+    }
+
+    _onChannelChanged: function(room) {
+        if (!room.channel) {
+            this._clearUsers();
+            return;
+        }
+
+        let members;
+        if (room.type == Tp.HandleType.ROOM)
+            members = room.channel.group_dup_members_contacts();
+        else
+            members = [room.channel.connection.self_contact, room.channel.target_contact];
+
+        members.forEach(m => { this._trackMember(m); });
+    },
+
+    _clearUsers: function() {
+        for (let [baseNick, contacts] of this._baseNickContacts)
+            contacts.slice().forEach((m) => { this._untrackMember(m); });
+    },
+
+    _onMemberRenamed: function(room, oldMember, newMember) {
+        this._untrackMember(oldMember);
+        this._trackMember(newMember);
+    },
+
+    _onMemberJoined: function(room, member) {
+        this._trackMember(member);
+    },
+
+    _onMemberLeft: function(room, member) {
+        this._untrackMember(member);
+    },
+
+    _pushMember: function(baseNick, member) {
+        if (!this._baseNickContacts.has(baseNick))
+            this._baseNickContacts.set(baseNick, []);
+        let contacts = this._baseNickContacts.get(baseNick);
+        return contacts.push(member);
+    },
+
+    _trackMember: function(member) {
+        let baseNick = Polari.util_get_basenick(member.alias);
+        let status = Tp.ConnectionPresenceType.AVAILABLE;
+
+        if (this._pushMember(baseNick, member) == 1)
+            this.emit("status-changed::" + baseNick, baseNick, status);
+    },
+
+    _popMember: function(baseNick, member) {
+        let contacts = this._baseNickContacts.get(baseNick) || [];
+        let index = contacts.map(c => c.alias).indexOf(member.alias);
+        if (index < 0)
+            return [false, contacts.length];
+        contacts.splice(index, 1);
+        return [true, contacts.length];
+    },
+
+    _untrackMember: function(member) {
+        let baseNick = Polari.util_get_basenick(member.alias);
+        let status = Tp.ConnectionPresenceType.OFFLINE;
+
+        let [found, nContacts] = this._popMember(baseNick, member);
+        if (found)
+            if (nContacts == 0)
+                this.emit("status-changed::" + baseNick, member.alias, status);
+    },
+
+    getNickStatus: function(nickName) {
+        let baseNick = Polari.util_get_basenick(nickName);
+
+        let contacts = this._baseNickContacts.get(baseNick) || [];
+        return contacts.length == 0 ? Tp.ConnectionPresenceType.OFFLINE
+                                    : Tp.ConnectionPresenceType.AVAILABLE;
+    }
+});


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