[polari/wip/raresv/nick-popover: 138/149] chatView: Split out status tracking



commit ee04efb54e1c3167a0302096a3d266198c1e05ad
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 33e9f42..8563072 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;
@@ -293,6 +294,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);
@@ -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();
@@ -448,8 +439,6 @@ const ChatView = new Lang.Class({
                 tag[prop] = tagProps[prop];
             }
         });
-
-        this._foreachNickTag(t => { this._updateTagStatus(t); });
     },
 
     vfunc_destroy: function() {
@@ -554,15 +543,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,40 +816,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;
@@ -891,19 +837,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 +861,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 +868,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 +876,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 +884,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 +898,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 +1120,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 +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)
@@ -1280,6 +1206,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..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]