[polari/wip/raresv/nick-popover: 6/16] userTracker: Add global tracking
- From: Rares Visalom <raresvisalom src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [polari/wip/raresv/nick-popover: 6/16] userTracker: Add global tracking
- Date: Mon, 5 Sep 2016 17:37:49 +0000 (UTC)
commit 1d7ef34f5b9b1f1a1e6df236b6a8acda5e490b11
Author: raresv <rares visalom gmail com>
Date: Tue Aug 2 00:46:26 2016 +0300
userTracker: Add global tracking
The first userTracker patch adds only the local tracking
functionality of the userTracker, but we still need the
global one too. This patch adds the global tracking
functionality. First of all we create the UserStatusMonitor,
a singleton that handles all other instances of UserTrackers.
UserTrackers are now account-specific, meaning that they
track rooms that are on the same account (network).
Obviously, each room is tracked both individually (local
tracking) and globally (global tracking). Global tracking
is achieved through emitting the status-changed::basenick
detailed signal while the local one uses custom callbacks.
The reason we use custom callbacks is that it avoids
filtering of the parameters sent by a signal. Local status
is in connection with both the basenick and the room, hence
double filtering would be required in the case of using
a detailed signal for the local tracking part.
Also, there is a new contacts-changed::basenick signal that
is emitted whenever the contacts change for a specific
basenick (useful when there are more users with the same
basenick).
src/chatView.js | 19 ++++-
src/userTracker.js | 214 +++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 200 insertions(+), 33 deletions(-)
---
diff --git a/src/chatView.js b/src/chatView.js
index ba21975..0b744bc 100644
--- a/src/chatView.js
+++ b/src/chatView.js
@@ -297,8 +297,8 @@ 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));
+ let statusMonitor = UserTracker.getUserStatusMonitor();
+ this._userTracker = statusMonitor.getUserTrackerForAccount(room.account);
this._room.account.connect('notify::nickname', Lang.bind(this,
function() {
@@ -351,6 +351,15 @@ const ChatView = new Lang.Class({
this._roomSignals.push(room.connect(signal.name, signal.handler));
}));
this._onChannelChanged();
+
+ this._nickStatusChangedId =
+ this._userTracker.watchRoomStatus(this._room, null,
+ Lang.bind(this, this._onNickStatusChanged));
+
+ this.connect('destroy', () => {
+ this._userTracker.unwatchRoomStatus(this._room, this._nickStatusChangedId);
+ this._userTracker = null;
+ });
},
_createTags: function() {
@@ -1168,6 +1177,7 @@ const ChatView = new Lang.Class({
this._view.get_buffer().get_tag_table().add(nickTag);
}
tags.push(nickTag);
+
if (needsGap)
tags.push(this._lookupTag('gap'));
this._insertWithTags(iter, message.nick + '\t', tags);
@@ -1202,8 +1212,9 @@ const ChatView = new Lang.Class({
this._insertWithTags(iter, text.substr(pos), tags);
},
- _onNickStatusChanged: function(tracker, nickName, status) {
- let nickTag = this._lookupTag(this._getNickTagName(nickName));
+ _onNickStatusChanged: function(baseNick, status) {
+ let nickTagName = this._getNickTagName(baseNick);
+ let nickTag = this._lookupTag(nickTagName);
if (!nickTag)
return;
diff --git a/src/userTracker.js b/src/userTracker.js
index 5f9ca55..a9d0db2 100644
--- a/src/userTracker.js
+++ b/src/userTracker.js
@@ -7,6 +7,52 @@ const Utils = imports.utils;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
+const AccountsMonitor = imports.accountsMonitor;
+const ChatroomManager = imports.chatroomManager;
+
+let _singleton = null;
+
+function getUserStatusMonitor() {
+ if (_singleton == null)
+ _singleton = new UserStatusMonitor();
+ return _singleton;
+}
+
+const UserStatusMonitor = new Lang.Class({
+ Name: 'UserStatusMonitor',
+
+ _init: function() {
+ this._userTrackers = new Map();
+ this._accountsMonitor = AccountsMonitor.getDefault();
+
+ this._accountsMonitor.connect('account-added', Lang.bind(this, this._onAccountAdded));
+ this._accountsMonitor.connect('account-removed', Lang.bind(this, this._onAccountRemoved));
+
+ this._accountsMonitor.dupAccounts().forEach(a => { this._onAccountAdded(this._accountsMonitor, a);
});
+ },
+
+ _onAccountAdded: function(accountsMonitor, account) {
+ if (this._userTrackers.has(account))
+ return;
+
+ this._userTrackers.set(account, new UserTracker(account));
+ },
+
+ _onAccountRemoved: function(accountsMonitor, account) {
+ if (!this._userTrackers.has(account))
+ return;
+
+ this._userTrackers.delete(account);
+ },
+
+ getUserTrackerForAccount: function(account) {
+ if (this._userTrackers.has(account))
+ return this._userTrackers.get(account);
+ return null;
+ }
+});
+
+
const UserTracker = new Lang.Class({
Name: 'UserTracker',
Extends: GObject.Object,
@@ -15,21 +61,45 @@ const UserTracker = new Lang.Class({
'status-changed': {
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(room) {
+ _init: function(account) {
this.parent();
+ this._account = account;
+
this._baseNickContacts = new Map();
+ this._roomData = new Map();
+ this._handlerCounter = 0;
- this._room = room;
+ this._chatroomManager = ChatroomManager.getDefault();
+ this._chatroomManager.connect('room-added', Lang.bind(this, this._onRoomAdded));
+ this._chatroomManager.connect('room-removed', Lang.bind(this, this._onRoomRemoved));
+ },
+
+ _getRoomContacts: function(room) {
+ return this._roomData.get(room).contactMapping;
+ },
+
+ _getRoomHandlers: function(room) {
+ return this._roomData.get(room).handlerMapping;
+ },
- this._onRoomAdded(this._room);
- this._onChannelChanged(this._room);
+ _getRoomSignals: function(room) {
+ return this._roomData.get(room).roomSignals;
},
- _onRoomAdded: function(room) {
+ _onRoomAdded: function(roomManager, room) {
+ if (room.account != this._account)
+ return;
+
+ this._ensureRoomMappingForRoom(room);
+
let roomSignals = [
{ name: 'notify::channel',
handler: Lang.bind(this, this._onChannelChanged) },
@@ -47,14 +117,24 @@ const UserTracker = new Lang.Class({
handler: Lang.bind(this, this._onMemberLeft) }
];
- roomSignals.forEach(Lang.bind(this, function(signal) {
- room.connect(signal.name, signal.handler);
- }));
- }
+ let signalIds = this._getRoomSignals(room);
+ roomSignals.forEach(signal => {
+ signalIds.push(room.connect(signal.name, signal.handler));
+ });
+ },
+
+ _onRoomRemoved: function(roomManager, room) {
+ if (!this._roomData.has(room))
+ return;
+
+ this._getRoomSignals(room).forEach(id => { room.disconnect(id); });
+ this._clearUsersFromRoom(room);
+ this._roomData.delete(room);
+ },
_onChannelChanged: function(room) {
if (!room.channel) {
- this._clearUsers();
+ this._clearUsersFromRoom(room);
return;
}
@@ -64,44 +144,70 @@ const UserTracker = new Lang.Class({
else
members = [room.channel.connection.self_contact, room.channel.target_contact];
- members.forEach(m => { this._trackMember(m); });
+ /*keep track of initial members in the room, both locally and
+ globally*/
+ members.forEach(m => { this._trackMember(m, room); });
},
- _clearUsers: function() {
- for ([baseNick, contacts] of this._baseNickContacts)
- contacts.slice().forEach((m) => { this._untrackMember(m); });
+ _clearUsersFromRoom: function(room) {
+ let map = this._getRoomContacts(room);
+ for ([baseNick, contacts] of map)
+ contacts.slice().forEach((m) => { this._untrackMember(m, room); });
+ },
+
+ _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) {
- this._untrackMember(oldMember);
- this._trackMember(newMember);
+ this._untrackMember(oldMember, room);
+ this._trackMember(newMember, room);
},
_onMemberJoined: function(room, member) {
- this._trackMember(member);
+ this._trackMember(member, room);
},
_onMemberLeft: function(room, member) {
- this._untrackMember(member);
+ this._untrackMember(member, room);
},
- _pushMember: function(baseNick, member) {
- if (!this._baseNickContacts.has(baseNick))
- this._baseNickContacts.set(baseNick, []);
- let contacts = this._baseNickContacts.get(baseNick);
+ _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);
+ },
+
+ _pushMember: function(map, baseNick, member) {
+ if (!map.has(baseNick))
+ map.set(baseNick, []);
+ let contacts = map.get(baseNick);
return contacts.push(member);
},
- _trackMember: function(member) {
+ _trackMember: function(member, room) {
let baseNick = Polari.util_get_basenick(member.alias);
let status = Tp.ConnectionPresenceType.AVAILABLE;
- if (this._pushMember(baseNick, member) == 1)
+ let map = this._baseNickContacts;
+ if (this._pushMember(map, baseNick, member) == 1)
this.emit("status-changed::" + baseNick, baseNick, status);
+
+ let roomMap = this._getRoomContacts(room);
+ if (this._pushMember(roomMap, baseNick, member) == 1)
+ this._runHandlers(room, member, status);
+
+ this.emit("contacts-changed::" + baseNick, member.alias);
},
- _popMember: function(baseNick, member) {
- let contacts = this._baseNickContacts.get(baseNick) || [];
+ _popMember: function(map, baseNick, member) {
+ let contacts = map.get(baseNick) || [];
let index = contacts.map(c => c.alias).indexOf(member.alias);
if (index < 0)
return [false, contacts.length];
@@ -109,14 +215,22 @@ const UserTracker = new Lang.Class({
return [true, contacts.length];
},
- _untrackMember: function(member) {
+ _untrackMember: function(member, room) {
let baseNick = Polari.util_get_basenick(member.alias);
let status = Tp.ConnectionPresenceType.OFFLINE;
- let [found, nContacts] = this._popMember(baseNick, member);
- if (found)
+ 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.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) {
@@ -125,5 +239,47 @@ const UserTracker = new Lang.Class({
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;
+ },
+
+ lookupContact: function(nickName) {
+ let baseNick = Polari.util_get_basenick(nickName);
+
+ let contacts = this._baseNickContacts.get(baseNick) || [];
+
+ if (contacts.length == 0)
+ return null;
+
+ for (let i = 0; i < contacts.length; i++)
+ if (contacts[i].alias == nickName)
+ return contacts[i];
+
+ return contacts[0];
+ },
+
+ watchRoomStatus: function(room, baseNick, callback) {
+ this._ensureRoomMappingForRoom(room);
+
+ this._getRoomHandlers(room).set(++this._handlerCounter, {
+ nickName: baseNick,
+ handler: callback
+ });
+
+ return this._handlerCounter;
+ },
+
+ unwatchRoomStatus: function(room, handlerID) {
+ if (!this._roomData.has(room))
+ return;
+ this._getRoomHandlers(room).delete(handlerID);
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]