FireTray/src/modules/FiretrayChat.jsm

247 lines
8.1 KiB
JavaScript

/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
var EXPORTED_SYMBOLS = [ "firetray" ];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource:///modules/imServices.jsm");
Cu.import("resource://firetray/commons.js");
Cu.import("resource://firetray/linux/FiretrayChatStatusIcon.jsm");
let log = firetray.Logging.getLogger("firetray.Chat");
firetray.Chat = {
initialized: false,
observedTopics: {},
acknowledgeOnFocus: {},
init: function() {
if (this.initialized) {
log.warn("Chat already initialized");
return;
}
log.debug("Enabling Chat");
firetray.Utils.addObservers(firetray.Chat, [
// "*", // debugging
"account-connected", "account-disconnected", "idle-time-changed",
"new-directed-incoming-message", "status-changed",
"unread-im-count-changed", "new-text"
]);
firetray.ChatStatusIcon.init();
this.updateIcon();
this.initialized = true;
},
shutdown: function() {
if (!this.initialized) return;
log.debug("Disabling Chat");
firetray.ChatStatusIcon.shutdown();
firetray.Utils.removeAllObservers(firetray.Chat);
this.initialized = false;
},
// FIXME: the listener should probably attached on the conv entry in the
// contactlist during startIconBlinkingMaybe
attachSelectListeners: function(win) {
log.debug("attachSelectListeners");
["contactlistbox", "tabmail"].forEach(function(eltId) {
win.document.getElementById(eltId)
.addEventListener('select', firetray.Chat.onSelect);
});
},
detachSelectListeners: function(win) {
["contactlistbox", "tabmail"].forEach(function(eltId) {
win.document.getElementById(eltId)
.removeEventListener('select', firetray.Chat.onSelect);
});
},
observe: function(subject, topic, data) {
log.debug("RECEIVED Chat: "+topic+" subject="+subject+" data="+data);
let conv = null;
switch (topic) {
case "account-connected":
case "account-disconnected":
case "idle-time-changed":
case "status-changed":
this.updateIcon();
break;
case "new-directed-incoming-message": // when PM or cited in channel
conv = subject.QueryInterface(Ci.prplIMessage).conversation;
log.debug("conversation name="+conv.name); // normalizedName shouldn't be necessary
this.startIconBlinkingMaybe(conv);
break;
/* Twitter is obviously considered a chatroom, not a private
conversation. This is why we need to detect incoming messages and switch
to the conversation differently. The actual read should be caught by
focus-in-event and 'select' event on tabmail and contactlist */
case "new-text":
conv = subject.QueryInterface(Ci.prplIMessage).conversation;
log.info("new-text from "+conv.title);
let proto = conv.account.QueryInterface(Ci.imIAccount).protocol;
if (proto.normalizedName === 'twitter')
this.startIconBlinkingMaybe(conv);
break;
case "unread-im-count-changed":
log.debug("unread-im-count-changed");
let unreadMsgCount = data;
if (unreadMsgCount == 0)
this.stopIconBlinkingMaybe();
let localizedTooltip = PluralForm.get(
unreadMsgCount,
firetray.Utils.strings.GetStringFromName("tooltip.unread_messages"))
.replace("#1", unreadMsgCount);
firetray.ChatStatusIcon.setIconTooltip(localizedTooltip);
break;
default:
log.warn("unhandled topic: "+topic);
}
},
// FIXME: implement a pool of conv which initiated a warning
startIconBlinkingMaybe: function(conv) {
let convIsCurrentlyShown = this.isConvCurrentlyShown(conv);
log.debug("convIsCurrentlyShown="+convIsCurrentlyShown);
if (!convIsCurrentlyShown) { // don't blink when conv tab already on top
this.acknowledgeOnFocus.must = true;
this.acknowledgeOnFocus.conv = conv;
firetray.ChatStatusIcon.setIconBlinking(true);
}
},
stopIconBlinkingMaybe: function(xid) { // xid optional
log.debug("acknowledgeOnFocus.must="+this.acknowledgeOnFocus.must);
if (!this.acknowledgeOnFocus.must) return;
let convIsCurrentlyShown = this.isConvCurrentlyShown(
this.acknowledgeOnFocus.conv, xid);
log.debug("convIsCurrentlyShown="+convIsCurrentlyShown);
if (this.acknowledgeOnFocus.must && convIsCurrentlyShown) {
log.debug("do stop icon blinking !!!");
firetray.ChatStatusIcon.setIconBlinking(false);
this.acknowledgeOnFocus.must = false;
}
},
onSelect: function(event) {
log.debug("select event ! ");
firetray.Chat.stopIconBlinkingMaybe();
},
isConvCurrentlyShown: function(conv, xid) {
log.debug("isConvCurrentlyShown");
let activeWin = xid || firetray.Handler.findActiveWindow();
if (!firetray.Handler.windows[activeWin]) return false;
log.debug("1 ***");
let activeChatTab = this.findSelectedChatTab(activeWin);
if (!activeChatTab) return false;
log.debug("2 ***");
/* for now there is only one Chat tab, so we don't need to
findSelectedChatTabFromTab(activeChatTab.tabNode). And, as there is only
one forlderPaneBox, there will also probably be only one contactlistbox
for all Chat tabs anyway */
let selectedConv = this.findSelectedConv(activeWin);
if (!selectedConv) return false;
log.debug("3 ***");
log.debug("conv.title='"+conv.title+"' selectedConv.title='"+selectedConv.title+"'");
return (conv.id == selectedConv.id);
},
findSelectedChatTab: function(xid) {
let win = firetray.Handler.windows[xid].chromeWin;
let tabmail = win.document.getElementById("tabmail");
let chatTabs = tabmail.tabModes.chat.tabs;
for each (let tab in chatTabs)
if (tab.tabNode.selected) return tab;
return null;
},
findSelectedConv: function(xid) {
let win = firetray.Handler.windows[xid].chromeWin;
let selectedItem = win.document.getElementById("contactlistbox").selectedItem;
if (!selectedItem || selectedItem.localName != "imconv") return null;
return selectedItem.conv;
},
updateIcon: function() {
let globalConnectedStatus = this.globalConnectedStatus();
let userStatus;
if (globalConnectedStatus)
userStatus = Services.core.globalUserStatus.statusType;
else
userStatus = Ci.imIStatusInfo.STATUS_OFFLINE;
log.debug("IM status="+userStatus);
let iconName;
switch (userStatus) {
case Ci.imIStatusInfo.STATUS_OFFLINE: // 1
iconName = FIRETRAY_IM_STATUS_OFFLINE;
break;
case Ci.imIStatusInfo.STATUS_IDLE: // 4
case Ci.imIStatusInfo.STATUS_AWAY: // 5
iconName = FIRETRAY_IM_STATUS_AWAY;
break;
case Ci.imIStatusInfo.STATUS_AVAILABLE: // 7
iconName = FIRETRAY_IM_STATUS_AVAILABLE;
break;
case Ci.imIStatusInfo.STATUS_UNAVAILABLE: // 6
iconName = FIRETRAY_IM_STATUS_BUSY;
break;
case Ci.imIStatusInfo.STATUS_UNKNOWN: // 0
case Ci.imIStatusInfo.STATUS_INVISIBLE: // 2
case Ci.imIStatusInfo.STATUS_MOBILE: // 3
default:
// ignore
}
log.debug("IM status changed="+iconName);
if (iconName)
firetray.ChatStatusIcon.setIconImage(iconName);
},
globalConnectedStatus: function() {
/* Because we may already be connected during init (for ex. when toggling
the chat_icon_enable pref), we need to updateIcon() during init(). But IM
accounts' list is not initialized at early stage... */
try {
let accounts = Services.accounts.getAccounts();
let globalConnected = false;
while (accounts.hasMoreElements()) {
let account = accounts.getNext().QueryInterface(Ci.imIAccount);
log.debug("account="+account+" STATUS="+account.statusInfo.statusType+" connected="+account.connected);
globalConnected = globalConnected || account.connected;
}
log.debug("globalConnected="+globalConnected);
return globalConnected;
} catch (e if e instanceof Components.Exception &&
e.result === Components.results.NS_ERROR_XPC_JS_THREW_JS_OBJECT &&
/_items is undefined/.test(e.message)) {
return false; // ignore
} catch(e) {
log.error(e); return false;
}
}
};