[polari/wip/raresv/userTrackerAndPopoversRebase: 20/24] userTracker: move notification logic from chatroomManager into userTracker.



commit 131f3371e3bfe526bfd56df7fb54e9e42dce57b3
Author: raresv <rares visalom gmail com>
Date:   Fri Aug 5 17:01:24 2016 +0300

    userTracker: move notification logic from chatroomManager into userTracker.
    
    userTracker: minor cleanup.
    
    userTracker: split out the function that ensures the
    fact that there is a contact mapping for the specified
    room and used this fufuncton in
    UserTracker.getBestMatchContactInRoom(). The reason for
    this is that any popover opened until polari is connected
    will result in an error (because the mapping was not
    ensured before).
    
    userTracker: add new contacts-changed::basenick detailed signal and emit it whenever the contacts change 
for a basenick. This is used to update the user in the UserDetails class. Rename getBestMatchingContactInRoom 
to lookupContact.
    
    userTracker: Add notifications-emitted signal (already removed in this patch).
    
    userTracker: add getNotifyActionName() method.
    
    userTracker: remove the notification-emitted signal, update the state of the notify action, update its 
enabled property and fix unwatchUser() method params.
    
    userTracker: replace old notification logic with GActions. Also fix a bug with the 
contacts-changed::basenick signal.
    
    userTracker: remove unnneded comment.
    
    userTracker: use format strings for notifications in
    order to be able to translate the text.
    
    userTracker: Base watchNick() on baseNick
    
    It doesn't really make sense to call watchNick() for a particular
    nickname rather than a basenick - the callback would only be called
    if the first contact that appears online matches the parameter.
    Rather than expecting callers to remember to call the method for
    a basenick, handle that ourselves.
    
    userTracker: Split out some code
    
    userTracker: Split out another helper
    
    userTracker: Clean up _trackMember()
    
    It currently takes a map parameter which is set to either the global
    or per-room mapping. But then it's always called in pairs for either
    map, and behaves differently based on which map it was called with.
    Just remove the map parameter and handle both maps internally.
    
    userTracker: Minor cleanup
    
    A contact is unique within a room, so if we remove a contact from
    a room mapping, we can just search by alias and don't need the
    room name.
    The same doesn't apply when we remove a contact from the global
    contacts list, where the same contact can be added for multiple
    rooms. But then we only really care about the number of contacts
    for a particular baseNick, so it doesn't matter if we just remove
    the first match ...
    
    userTracker: Give _clearUsersFromRoom() the _trackMember() treatment
    
    userTracker: Clean up _untrackMember as well
    
    userTracker: more cleanups
    
    userTracker: Cut-down on signals handlers
    
    We don't care about whether a user left or was kicked/banned - just
    use the same handler for all those signals.
    
    userTracker: Don't reset handlerCounter when ensuring handlerMapping
    
    userTracker: Simplify some more
    
    userTracker: Don't run handlers twice on disconnect
    
    _untrackMember() already calls the handlers for us, no need for a repeat
    run ...
    
    userTracker: Remove unnecessary method split
    
    userTracker: Be a bit more concise
    
    Variables don't exist in a vacuum, we have context - if a method is
    called getNotifyActionName(), it's clear enough that 'name' and 'action'
    don't refer to a frobnicate action ...
    
    userTracker: Add some comments
    
    userTracker: minor fixes.
    
    userTracker: rename _userTrackerMapping to _userTrackers.
    userTracker: send nickname along with the contacts-changed signal.
    userTracker: store room signals in a local variable, not in a class property.
    userTracker: rename the _globalContactsMapping Map to _baseNickContacts.
    userTracker: rename emittingRoom to room.
    userTracker: send nickname along with the contacts-changed signal.
    userTracker: store room signals in a local variable, not in a class property.
    userTracker: rename the _globalContactsMapping Map to _baseNickContacts.
    userTracker: rename emittingRoom to room.
    userTracker: clean _onChannelChanged() by saving one level of indentation.
    userTracker: move getNickRoomStatus() method near the getNickStatus() method.
    userTracker: rename (un)watchUser() to (un)watchRoomStatus().
    userTracker: rewrite the way the _handlerCounter is user. This makes it more readable
    userTracker: rename emitWatchedUserNotification() to _emitNotification().
    userTracker: getNotifyActionName() now returns the full name of the action and the track/untrack methods 
are cleaner.
    userTracker: fix consistency issues regarding the use of
    basenick instead of member.alias (aka nickname).
    userTracker: use the enabled property of the GAction that's used for notifications. This fixes the 
notifyButton visibility bug. The notifyButton would remain visible even when the user was online.
    
    userTracker: squashed commit of multiple userTracker
    commits.
    
    userTracker: Fix exceptions when leaving room
    
    When we only guard signal disconnections with the test for the right
    account, we end up trying to clear out room data from accounts that
    are not tracking that room ...
    
    userTracker: Don't clear out all room data on disconnect
    
    Disconnecting means tracked users for that room are no longer valid
    and need to be cleared out, but both watch handlers and room signal
    IDs are still valid and we need to keep them.
    
    userTracker: Fix untracking members on disconnect
    
    Array.forEach() doesn't handle array changes while running the loop,
    so replace it with a while loop that can deal with the changing
    array size.
    
    userTracker: Don't split out disconnectSignals() method
    
    It can be replaced by a one-liner in the only place it is used ...
    
    userTracker: Same for deleteRoomData ...
    
    userTracker: Minor cleanup
    
    When a room is removed, rather than duplicating the condition for
    whether the room is tracked, we can just check whether we have
    room data for it - everything the function does will be useless
    anyway if we don't have any data to operate on ...
    
    userTracker: replace while loop with forEach
    
    userTracker: Remove connectRoomSignals()
    
    Splitting out the code doesn't add any clarity over just setting
    up signals in onRoomAdded() ...
    
    userTracker: Remove some roomData helpers
    
    getRoomContacts(), getRoomHandlers() and getRoomSignals() hide the
    nested maps in roomData, which helps making the code more readable.
    However wrapping the Map() API for roomData itself only hides that
    roomData is a map, which doesn't seem useful.
    
    userTracker: Remove unneeded call to ensureRoomMapping()
    
    _onChannelChanged() is only run when we connected to the 'notify::channel'
    signal - we store the handler ID in the roomData, so the data must exist
    when the handler is connected.
    
    userTracker: Don't prefix roomData properties
    
    this._roomData.get(room)._contactMapping looks "wrong", namely like
    accessing something else's private property. Prefixing roomData
    itself already makes it clear that other module's don't have any
    business in poking around the property, so we don't need to prefix
    the individual properties anyway ...
    
    userTracker: renamed _emitNotification() to _notifyNickAvailable() and swapped its parmas
    
    userTracker: renamed _getNotifyActionName() to _getNotifyActionNameInternal()
    
    userTracker: rename _roomMapping Map to _roomData and provide some helper methods to access the property.
    
    userTracker: use the notify action name as an id for the notification.

 src/userTracker.js |  426 +++++++++++++++++++++++++--------------------------
 1 files changed, 209 insertions(+), 217 deletions(-)
---
diff --git a/src/userTracker.js b/src/userTracker.js
index 7e78bbd..5f53f2e 100644
--- a/src/userTracker.js
+++ b/src/userTracker.js
@@ -3,6 +3,9 @@ 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 AccountsMonitor = imports.accountsMonitor;
 const ChatroomManager = imports.chatroomManager;
@@ -19,7 +22,7 @@ const UserStatusMonitor = new Lang.Class({
     Name: 'UserStatusMonitor',
 
     _init: function() {
-        this._userTrackersMaping = new Map();
+        this._userTrackers = new Map();
         this._accountsMonitor = AccountsMonitor.getDefault();
 
         this._accountsMonitor.connect('account-added', Lang.bind(this, this._onAccountAdded));
@@ -27,30 +30,22 @@ const UserStatusMonitor = new Lang.Class({
     },
 
     _onAccountAdded: function(accountsMonitor, account) {
-        this._addUserTrackerForAccount(account);
-    },
-
-    _onAccountRemoved: function(accountsMonitor, account) {
-        this._removeUserTrackerForAccount(account);
-    },
-
-    _addUserTrackerForAccount: function(account) {
-        if (this._userTrackersMaping.has(account))
+        if (this._userTrackers.has(account))
             return;
 
-        this._userTrackersMaping.set(account, new UserTracker(account));
+        this._userTrackers.set(account, new UserTracker(account));
     },
 
-    _removeUserTrackerForAccount: function(account) {
-        if (!this._userTrackersMaping.has(account))
+    _onAccountRemoved: function(accountsMonitor, account) {
+        if (!this._userTrackers.has(account))
             return;
 
-        this._userTrackersMaping.delete(account);
+        this._userTrackers.delete(account);
     },
 
     getUserTrackerForAccount: function(account) {
-        if (this._userTrackersMaping.has(account))
-            return this._userTrackersMaping.get(account);
+        if (this._userTrackers.has(account))
+            return this._userTrackers.get(account);
         return null;
     }
 });
@@ -65,250 +60,209 @@ const UserTracker = new Lang.Class({
             flags: GObject.SignalFlags.DETAILED,
             param_types: [GObject.TYPE_STRING, GObject.TYPE_INT]
         },
+        'contacts-changed': {
+            flags: GObject.SignalFlags.DETAILED,
+            param_types: [GObject.TYPE_STRING]
+        }
     },
 
     _init: function(account) {
         this.parent();
-        this._referenceRoomSignals = [
-            { 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._onMemberDisconnected) },
-            { name: 'member-kicked',
-              handler: Lang.bind(this, this._onMemberKicked) },
-            { name: 'member-banned',
-              handler: Lang.bind(this, this._onMemberBanned) },
-            { name: 'member-joined',
-              handler: Lang.bind(this, this._onMemberJoined) },
-            { name: 'member-left',
-              handler: Lang.bind(this, this._onMemberLeft) }
-        ];
 
         this._account = account;
 
-        this._globalContactMapping = new Map();
-        this._roomMapping = new Map();
+        this._baseNickContacts = new Map();
+        this._roomData = new Map();
         this._handlerCounter = 0;
-
-        this._userStatusMonitor = getUserStatusMonitor();
+        this._app = Gio.Application.get_default();
 
         this._chatroomManager = ChatroomManager.getDefault();
         this._chatroomManager.connect('room-added', Lang.bind(this, this._onRoomAdded));
         this._chatroomManager.connect('room-removed', Lang.bind(this, this._onRoomRemoved));
     },
 
-    _onRoomAdded: function(roomManager, room) {
-        if (room.account == this._account)
-            this._connectRoomSignalsForRoom(room);
+    _getRoomContacts: function(room) {
+        return this._roomData.get(room).contactMapping;
     },
 
-    _onRoomRemoved: function(roomManager, room) {
-        if (room.account == this._account)
-            this._disconnectRoomSignalsForRoom(room);
+    _getRoomHandlers: function(room) {
+        return this._roomData.get(room).handlerMapping;
+    },
 
-        this._clearUsersFromRoom(this._globalContactMapping, room);
-        this._clearUsersFromRoom(this._roomMapping.get(room)._contactMapping, room);
+    _getRoomSignals: function(room) {
+        return this._roomData.get(room).roomSignals;
     },
 
-    _connectRoomSignalsForRoom: function(room) {
+    _onRoomAdded: function(roomManager, room) {
+        if (room.account != this._account)
+            return;
+
         this._ensureRoomMappingForRoom(room);
 
-        let roomData = this._roomMapping.get(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) }
+        ];
 
-        roomData._roomSignals = [];
-        this._referenceRoomSignals.forEach(Lang.bind(this, function(signal) {
-            roomData._roomSignals.push(room.connect(signal.name, signal.handler));
-        }));
+        let signalIds = this._getRoomSignals(room);
+        roomSignals.forEach(signal => {
+            signalIds.push(room.connect(signal.name, signal.handler));
+        });
     },
 
-    _disconnectRoomSignalsForRoom: function(room) {
-        let roomData = this._roomMapping.get(room);
+    _onRoomRemoved: function(roomManager, room) {
+        if (!this._roomData.has(room))
+            return;
 
-        for (let i = 0; i < roomData._roomSignals.length; i++) {
-            room.disconnect(roomData._roomSignals[i]);
-        }
-        roomData._roomSignals = [];
+        this._getRoomSignals(room).forEach(id => { room.disconnect(id); });
+        this._clearUsersFromRoom(room);
+        this._roomData.delete(room);
     },
 
-    _onChannelChanged: function(emittingRoom) {
-        if (emittingRoom.channel) {
-            let members;
-            if (emittingRoom.type == Tp.HandleType.ROOM)
-                members = emittingRoom.channel.group_dup_members_contacts();
-            else
-                members = [emittingRoom.channel.connection.self_contact, 
emittingRoom.channel.target_contact];
-
-            /*is this needed here?*/
-            this._ensureRoomMappingForRoom(emittingRoom);
-
-            /*if there is no map keeping track of the users in the emittingRoom
-            create it*/
-            if (!this._roomMapping.get(emittingRoom)._contactMapping)
-                this._roomMapping.get(emittingRoom)._contactMapping = new Map();
-
-            /*if there is no map keeping track of the local status change handlers*/
-            this._ensureHandlerMappingForRoom(emittingRoom);
-
-            /*keep track of initial members in the emittingRoom, both locally and
-            globally*/
-            members.forEach(m => {
-                m._room = emittingRoom;
-                this._trackMember(this._roomMapping.get(emittingRoom)._contactMapping, m, emittingRoom);
-                this._trackMember(this._globalContactMapping, m, emittingRoom);
-            });
-        } else {
-            /*handle the absence of a channel for the global case*/
-            this._clearUsersFromRoom(this._globalContactMapping, emittingRoom);
-            /*handle the absence of a channel for the local case*/
-            this._clearUsersFromRoom(this._roomMapping.get(emittingRoom)._contactMapping, emittingRoom);
-
-            /*since we have no channel, all users must be locally marked offline. so call the callbacks*/
-            for ([handlerID, handlerInfo] of this._roomMapping.get(emittingRoom)._handlerMapping) {
-                if (handlerInfo.nickName)
-                    handlerInfo.handler(handlerInfo.nickName, Tp.ConnectionPresenceType.OFFLINE);
-            }
+    _onChannelChanged: function(room) {
+        if (!room.channel) {
+            this._clearUsersFromRoom(room);
+            return;
         }
-    },
 
-    _clearUsersFromRoom: function(mapping, room) {
-        for ([baseNick, basenickContacts] of mapping) {
-            basenickContacts.forEach(Lang.bind(this, function(member) {
-                if (member._room == room)
-                    /*safe to delete while iterating?*/
-                    this._untrackMember(mapping, member, room);
-            }));
+        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];
 
-            mapping.delete(baseNick);
-        }
+        /*keep track of initial members in the room, both locally and
+        globally*/
+        members.forEach(m => { this._trackMember(m, room); });
     },
 
-    _ensureRoomMappingForRoom: function(room) {
-        if (!this._roomMapping.has(room))
-            this._roomMapping.set(room, {});
+    _clearUsersFromRoom: function(room) {
+        let map = this._getRoomContacts(room);
+        for ([baseNick, contacts] of map)
+            contacts.slice().forEach((m) => { this._untrackMember(m, room); });
     },
 
-    _ensureHandlerMappingForRoom: function(room) {
-        /*if there is no map keeping track of the local status change handlers*/
-        if (!this._roomMapping.get(room)._handlerMapping) {
-            this._roomMapping.get(room)._handlerMapping = new Map();
-            this._handlerCounter = 0;
-        }
+    _ensureRoomMappingForRoom: function(room) {
+        if (this._roomData.has(room))
+            return;
+        this._roomData.set(room, { contactMapping: new Map(),
+                                   handlerMapping: new Map(),
+                                   roomSignals: [] });
     },
 
     _onMemberRenamed: function(room, oldMember, newMember) {
-        oldMember._room = room;
-        newMember._room = room;
-
-        this._untrackMember(this._roomMapping.get(room)._contactMapping, oldMember, room);
-        this._untrackMember(this._globalContactMapping, oldMember, room);
-        this._trackMember(this._roomMapping.get(room)._contactMapping, newMember, room);
-        this._trackMember(this._globalContactMapping, newMember, room);
+        this._untrackMember(oldMember, room);
+        this._trackMember(newMember, room);
     },
 
-    _onMemberDisconnected: function(room, member, message) {
-        member._room = room;
-
-        this._untrackMember(this._roomMapping.get(room)._contactMapping, member, room);
-        this._untrackMember(this._globalContactMapping, member, room);
+    _onMemberJoined: function(room, member) {
+        this._trackMember(member, room);
     },
 
-    _onMemberKicked: function(room, member, actor) {
-        member._room = room;
-
-        this._untrackMember(this._roomMapping.get(room)._contactMapping, member, room);
-        this._untrackMember(this._globalContactMapping, member, room);
+    _onMemberLeft: function(room, member) {
+        this._untrackMember(member, room);
     },
 
-    _onMemberBanned: function(room, member, actor) {
-        member._room = room;
-
-        this._untrackMember(this._roomMapping.get(room)._contactMapping, member, room);
-        this._untrackMember(this._globalContactMapping, member, room);
+    _runHandlers: function(room, member, status) {
+        let baseNick = Polari.util_get_basenick(member.alias);
+        let roomHandlers = this._getRoomHandlers(room);
+        for ([id, info] of roomHandlers)
+            if (!info.nickName || info.nickName == baseNick)
+                info.handler(baseNick, status);
     },
 
-    _onMemberJoined: function(room, member) {
-        member._room = room;
-
-        this._trackMember(this._roomMapping.get(room)._contactMapping, member, room);
-        this._trackMember(this._globalContactMapping, member, room);
+    _pushMember: function(map, baseNick, member) {
+        if (!map.has(baseNick))
+            map.set(baseNick, []);
+        let contacts = map.get(baseNick);
+        return contacts.push(member);
     },
 
-    _onMemberLeft: function(room, member, message) {
-        member._room = room;
+    _trackMember: function(member, room) {
+        let baseNick = Polari.util_get_basenick(member.alias);
+        let status = Tp.ConnectionPresenceType.AVAILABLE;
 
-        this._untrackMember(this._roomMapping.get(room)._contactMapping, member, room);
-        this._untrackMember(this._globalContactMapping, member, room);
-    },
+        let map = this._baseNickContacts;
+        if (this._pushMember(map, baseNick, member) == 1) {
+            this.emit("status-changed::" + baseNick, baseNick, status);
 
-    _trackMember: function(map, member, room) {
-        let baseNick = Polari.util_get_basenick(member.alias);
+            if (this._shouldNotifyNick(member.alias))
+                this._notifyNickAvailable(member, room);
 
-        if (map.has(baseNick))
-            map.get(baseNick).push(member);
-        else
-            map.set(baseNick, [member]);
+            this._setNotifyActionEnabled(member.alias, false);
+        }
 
-        //was on HEAD
-        /*if (this._contactMapping.get(baseNick).length == 1)
-            this.emit("status-changed::"+baseNick, member.alias, Tp.ConnectionPresenceType.AVAILABLE);*/
-        if (map == this._globalContactMapping)log("length: " + 
this._globalContactMapping.get(baseNick).length)
+        let roomMap = this._getRoomContacts(room);
+        if (this._pushMember(roomMap, baseNick, member) == 1)
+            this._runHandlers(room, member, status);
 
-        if (map.get(baseNick).length == 1)
-            if (map == this._globalContactMapping) {
-                this.emit("global-status-changed::" + member.alias, Tp.ConnectionPresenceType.AVAILABLE);
-                log("[global status] user " + member.alias + " is globally online");
-            }
-            else
-                //log("[Local UserTracker] User " + member.alias + " is now available in room " + 
member._room.channelName + " on " + this._account.get_display_name());
-                for ([handlerID, handlerInfo] of this._roomMapping.get(room)._handlerMapping)
-                    if (handlerInfo.nickName == member.alias)
-                        handlerInfo.handler(handlerInfo.nickName, Tp.ConnectionPresenceType.AVAILABLE);
-                    else if (!handlerInfo.nickName)
-                        handlerInfo.handler(member.alias, Tp.ConnectionPresenceType.AVAILABLE);
+        this.emit("contacts-changed::" + baseNick, member.alias);
     },
 
-    _untrackMember: function(map, member, room) {
-        let baseNick = Polari.util_get_basenick(member.alias);
-
+    _popMember: function(map, baseNick, member) {
         let contacts = map.get(baseNick) || [];
-        /*i really don't like this search. maybe use a for loop?*/
-        let indexToDelete = contacts.map(c => c.alias + "|" + c._room.channelName).indexOf(member.alias + 
"|" + member._room.channelName);
-
-        if (indexToDelete > -1) {
-            let removedMember = contacts.splice(indexToDelete, 1)[0];
-
-            if (contacts.length == 0)
-                //was on HEAD
-                /*this.emit("status-changed::"+baseNick, member.alias, Tp.ConnectionPresenceType.OFFLINE);*/
-                if (map == this._globalContactMapping) {
-                    this.emit("global-status-changed::" + member.alias, Tp.ConnectionPresenceType.OFFLINE);
-                    log("[global status] user " + member.alias + " is globally offline");
-                }
-                else
-                    //log("[Local UserTracker] User " + member.alias + " is now offline in room " + 
member._room.channelName + " on " + this._account.get_display_name());
-                    for ([handlerID, handlerInfo] of this._roomMapping.get(room)._handlerMapping)
-                        if (handlerInfo.nickName == member.alias)
-                            handlerInfo.handler(handlerInfo.nickName, Tp.ConnectionPresenceType.OFFLINE);
-                        else if (!handlerInfo.nickName)
-                            handlerInfo.handler(member.alias, Tp.ConnectionPresenceType.OFFLINE);
+        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, room) {
+        let baseNick = Polari.util_get_basenick(member.alias);
+        let status = Tp.ConnectionPresenceType.OFFLINE;
+
+        let map = this._baseNickContacts;
+        let [found, nContacts] = this._popMember(map, baseNick, member);
+        if (found) {
+            if (nContacts == 0) {
+                this.emit("status-changed::" + baseNick, member.alias, status);
+                this._setNotifyActionEnabled(member.alias, true);
+            }
+            this.emit("contacts-changed::" + baseNick, member.alias);
         }
+
+        let roomMap = this._getRoomContacts(room);
+        [found, nContacts] = this._popMember(roomMap, baseNick, member);
+        if (found && nContacts == 0)
+            this._runHandlers(room, member, status);
     },
 
     getNickStatus: function(nickName) {
         let baseNick = Polari.util_get_basenick(nickName);
 
-        let contacts = this._globalContactMapping.get(baseNick) || [];
+        let contacts = this._baseNickContacts.get(baseNick) || [];
+        return contacts.length == 0 ? Tp.ConnectionPresenceType.OFFLINE
+                                    : Tp.ConnectionPresenceType.AVAILABLE;
+    },
+
+    getNickRoomStatus: function(nickName, room) {
+        let baseNick = Polari.util_get_basenick(nickName);
+
+        this._ensureRoomMappingForRoom(room);
+
+        let contacts = this._getRoomContacts(room).get(baseNick) || [];
         return contacts.length == 0 ? Tp.ConnectionPresenceType.OFFLINE
                                     : Tp.ConnectionPresenceType.AVAILABLE;
     },
 
-    getBestMatchingContact: function(nickName) {
+    lookupContact: function(nickName) {
         let baseNick = Polari.util_get_basenick(nickName);
-        let contacts = this._contactMapping.get(baseNick) || [];
 
-        /*even possible?*/
+        let contacts = this._baseNickContacts.get(baseNick) || [];
+
         if (contacts.length == 0)
             return null;
 
@@ -319,41 +273,79 @@ const UserTracker = new Lang.Class({
         return contacts[0];
     },
 
-    getNickRoomStatus: function(nickName, room) {
-        let baseNick = Polari.util_get_basenick(nickName);
-
-        let contacts = this._roomMapping.get(room)._contactMapping.get(baseNick) || [];
-        return contacts.length == 0 ? Tp.ConnectionPresenceType.OFFLINE
-                                    : Tp.ConnectionPresenceType.AVAILABLE;
-    },
-
-    watchUser: function(room, nick, callback) {
+    watchRoomStatus: function(room, baseNick, callback) {
         this._ensureRoomMappingForRoom(room);
-        this._ensureHandlerMappingForRoom(room);
 
-        this._roomMapping.get(room)._handlerMapping.set(this._handlerCounter, {
-            nickName: nick,
+        this._getRoomHandlers(room).set(++this._handlerCounter, {
+            nickName: baseNick,
             handler: callback
         });
 
-        this._handlerCounter++;
+        return this._handlerCounter;
+    },
 
-        return this._handlerCounter - 1;
+    unwatchRoomStatus: function(room, handlerID) {
+        if (!this._roomData.has(room))
+            return;
+        this._getRoomHandlers(room).delete(handlerID);
     },
 
-    unwatchUser: function(room, nick, handlerID) {
-        /*it wouldn't make sense to call _ensure() here, right?*/
+    _notifyNickAvailable: function (member, room) {
+        let notification = new Gio.Notification();
+        notification.set_title(_("User is online"));
+        notification.set_body(_("User %s is now online.").format(member.alias));
 
-        /*rewrite into a single conditional?*/
-        if (!this._roomMapping)
-            return;
+        let param = GLib.Variant.new('(ssu)',
+                                     [ this._account.get_object_path(),
+                                       room.channel_name,
+                                       Utils.getTpEventTime() ]);
+        notification.set_default_action_and_target('app.join-room', param);
 
-        if (!this._roomMapping.has(room))
-            return;
+        this._app.send_notification(this._getNotifyActionNameInternal(member.alias), notification);
 
-        if (!this._roomMapping.get(room)._handlerMapping)
-            return;
+        let baseNick = Polari.util_get_basenick(member.alias);
+    },
+
+    _shouldNotifyNick: function(nickName) {
+        let actionName = this._getNotifyActionNameInternal(nickName);
+        let state = this._app.get_action_state(actionName);
+        return state ? state.get_boolean()
+                     : false;
+    },
+
+    _setNotifyActionEnabled: function(nickName, enabled) {
+        let name = this._getNotifyActionNameInternal(nickName);
+        let action = this._app.lookup_action(name);
+        if (action)
+            action.enabled = enabled;
+    },
+
+    _getNotifyActionNameInternal: function(nickName) {
+        return 'notify-user-' +
+               this._account.get_path_suffix() + '-' +
+               Polari.util_get_basenick(nickName);
+    },
+
+    getNotifyActionName: function(nickName) {
+        let name = this._getNotifyActionNameInternal(nickName);
+
+        if (!this._app.lookup_action(name)) {
+            let status = this.getNickStatus(nickName);
+            let enabled = status == Tp.ConnectionPresenceType.OFFLINE;
+
+            let state = new GLib.Variant('b', false);
+            let action = new Gio.SimpleAction({ name: name,
+                                                enabled: enabled,
+                                                state: state });
+
+            action.connect('notify::enabled', () => {
+                if (!action.enabled)
+                    action.change_state(GLib.Variant.new('b', false));
+            });
+
+            this._app.add_action(action);
+        }
 
-        this._roomMapping.get(room)._handlerMapping.delete(handlerID);
+        return 'app.' + name;
     }
 });


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