[polari/wip/raresv/GSoC: 1/17] userTracker: add UserTracker class with local tracking only.



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

    userTracker: add UserTracker class with local tracking only.
    
    Polari needs a new module, a stand-alone way of tracking user
    status across rooms. As the first part of the new UserTracker
    module, this patch adds the local tracking functionality.
    The local tracking part of the userTracker tracks status
    changes that happen only inside a given room. Status changes
    are handled with the status-changed::basenick detailed signal.
    As a result, the tracking part that resided in the chatView
    is now moved to the new userTracker module.

 src/chatView.js                        |  112 ++++++++--------------------
 src/org.gnome.Polari.src.gresource.xml |    1 +
 src/userTracker.js                     |  129 ++++++++++++++++++++++++++++++++
 3 files changed, 160 insertions(+), 82 deletions(-)
---
diff --git a/src/chatView.js b/src/chatView.js
index 867a7a7..08f823a 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();
 
@@ -1236,10 +1161,8 @@ const ChatView = new Lang.Class({
                 let nickTag = this._lookupTag(nickTagName);
 
                 if (!nickTag) {
-                    nickTag = new Gtk.TextTag({ name: nickTagName });
+                    nickTag = this._createNickTag(message.nick);
                     this._view.get_buffer().get_tag_table().add(nickTag);
-
-                    this._resetNickTag(nickTag);
                 }
                 tags.push(nickTag);
                 if (needsGap)
@@ -1276,6 +1199,31 @@ 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;
+    },
+
+    _createNickTag: function(nickName) {
+        let nickTagName = this._getNickTagName(nickName);
+
+        let tag = new Gtk.TextTag({ name: nickTagName });
+        this._updateNickTag(tag, this._userTracker.getNickStatus(nickName));
+
+        return tag;
+    },
+
     _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]