[polari/wip/raresv/nick-popover: 5/16] chatView: Split out status tracking.



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

    chatView: Split out status tracking.
    
    As we want a new stand-alone way of tracking user status across
    rooms, we want this code to be placed separately from other
    modules in the app. As consequence, all the code that is related
    to user status tracking needs to be split out from the ChatView
    and moved to the new UserTracker module.

 src/chatView.js                        |  104 ++++++--------------------
 src/org.gnome.Polari.src.gresource.xml |    1 +
 src/userTracker.js                     |  129 ++++++++++++++++++++++++++++++++
 3 files changed, 153 insertions(+), 81 deletions(-)
---
diff --git a/src/chatView.js b/src/chatView.js
index 867a7a7..ba21975 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;
@@ -296,6 +297,9 @@ const ChatView = new Lang.Class({
         this._pendingLogs = [];
         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);
@@ -384,19 +388,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();
@@ -450,8 +441,6 @@ const ChatView = new Lang.Class({
                 tag[prop] = tagProps[prop];
             }
         });
-
-        this._foreachNickTag(t => { this._updateTagStatus(t); });
     },
 
     vfunc_destroy: function() {
@@ -513,15 +502,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 _nPending() {
@@ -785,40 +765,6 @@ 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;
-    },
-
     _onChannelChanged: function() {
         if (this._channel == this._room.channel)
             return;
@@ -835,19 +781,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;
 
@@ -875,8 +808,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) {
@@ -884,7 +815,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) {
@@ -893,7 +823,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) {
@@ -902,13 +831,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) {
@@ -918,7 +845,6 @@ const ChatView = new Lang.Class({
             text += ' (%s)'.format(message);
 
         this._insertStatus(text, member.alias, 'left');
-        this._untrackContact(member);
     },
 
     _onMessageReceived: function(room, tpMessage) {
@@ -1152,7 +1078,6 @@ const ChatView = new Lang.Class({
 
         let iter = this._view.buffer.get_end_iter();
         this._insertMessage(iter, message, this._state);
-        this._trackContact(tpMessage.sender);
 
         let [id, valid] = tpMessage.get_pending_message_id();
 
@@ -1237,9 +1162,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)
@@ -1276,6 +1202,22 @@ const ChatView = new Lang.Class({
         this._insertWithTags(iter, text.substr(pos), tags);
     },
 
+    _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 6acfd23..9a37622 100644
--- a/src/org.gnome.Polari.src.gresource.xml
+++ b/src/org.gnome.Polari.src.gresource.xml
@@ -19,5 +19,6 @@
     <file>tabCompletion.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..5f9ca55
--- /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 ([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]