converse.js
Version:
Browser based XMPP chat client
180 lines (164 loc) • 5.88 kB
JavaScript
import _converse from '../../shared/_converse.js';
import api from '../../shared/api/index.js';
import converse from '../../shared/api/public.js';
import { initStorage } from '../../utils/storage.js';
import { getUnloadEvent } from '../../utils/session.js';
import { ACTIVE, INACTIVE } from '../../shared/constants.js';
const { Strophe, $build } = converse.env;
/**
* @param {boolean} reconnecting
*/
function onStatusInitialized(reconnecting) {
/**
* Triggered when the user's own chat status has been initialized.
* @event _converse#statusInitialized
* @example _converse.api.listen.on('statusInitialized', status => { ... });
* @example _converse.api.waitUntil('statusInitialized').then(() => { ... });
*/
api.trigger('statusInitialized', reconnecting);
}
/**
* @param {boolean} reconnecting
*/
export function initStatus(reconnecting) {
// If there's no profile obj, then we were never connected to
// begin with, so we set reconnecting to false.
reconnecting = _converse.state.profile === undefined ? false : reconnecting;
if (reconnecting) {
onStatusInitialized(reconnecting);
} else {
const id = `converse.xmppstatus-${_converse.session.get('bare_jid')}`;
_converse.state.profile = new _converse.exports.Profile({ id });
_converse.state.xmppstatus = _converse.state.profile; // Deprecated
Object.assign(_converse, { xmppstatus: _converse.state.profile }); // Deprecated
initStorage(_converse.state.profile, id, 'session');
_converse.state.profile.fetch({
success: () => onStatusInitialized(reconnecting),
error: () => onStatusInitialized(reconnecting),
silent: true,
});
}
}
let idle_seconds = 0;
let idle = false;
let auto_changed_status = false;
let inactive = false;
export function isIdle() {
return idle;
}
export function getIdleSeconds() {
return idle_seconds;
}
/**
* Resets counters and flags relating to CSI and auto_away/auto_xa
*/
export function onUserActivity() {
if (idle_seconds > 0) {
idle_seconds = 0;
}
if (!api.connection.get()?.authenticated) {
// We can't send out any stanzas when there's no authenticated connection.
// This can happen when the connection reconnects.
return;
}
if (inactive) sendCSI(ACTIVE);
if (idle) {
idle = false;
api.user.presence.send();
}
if (auto_changed_status === true) {
auto_changed_status = false;
// XXX: we should really remember the original state here, and
// then set it back to that...
_converse.state.profile.set('show', undefined);
}
}
/**
* An interval handler running every second.
* Used for CSI and the auto_away and auto_xa features.
*/
export function onEverySecond() {
if (!api.connection.get()?.authenticated) {
// We can't send out any stanzas when there's no authenticated connection.
// This can happen when the connection reconnects.
return;
}
const { profile } = _converse.state;
const show = profile.get('show');
idle_seconds++;
if (api.settings.get('csi_waiting_time') > 0 && idle_seconds > api.settings.get('csi_waiting_time') && !inactive) {
sendCSI(INACTIVE);
}
if (
api.settings.get('idle_presence_timeout') > 0 &&
idle_seconds > api.settings.get('idle_presence_timeout') &&
!idle
) {
idle = true;
api.user.presence.send();
}
if (
api.settings.get('auto_away') > 0 &&
idle_seconds > api.settings.get('auto_away') &&
show !== 'away' &&
show !== 'xa' &&
show !== 'dnd'
) {
auto_changed_status = true;
profile.set('show', 'away');
} else if (
api.settings.get('auto_xa') > 0 &&
idle_seconds > api.settings.get('auto_xa') &&
show !== 'xa' &&
show !== 'dnd'
) {
auto_changed_status = true;
profile.set('show', 'xa');
}
}
/**
* Send out a Client State Indication (XEP-0352)
* @function sendCSI
* @param { String } stat - The user's chat status
*/
export function sendCSI(stat) {
api.send($build(stat, { xmlns: Strophe.NS.CSI }));
inactive = stat === INACTIVE ? true : false;
}
let everySecondTrigger;
/**
* Set an interval of one second and register a handler for it.
* Required for the auto_away, auto_xa and csi_waiting_time features.
*/
export function registerIntervalHandler() {
if (
api.settings.get('auto_away') < 1 &&
api.settings.get('auto_xa') < 1 &&
api.settings.get('csi_waiting_time') < 1 &&
api.settings.get('idle_presence_timeout') < 1
) {
// Waiting time of less then one second means features aren't used.
return;
}
idle_seconds = 0;
auto_changed_status = false; // Was the user's status changed by Converse?
const { onUserActivity, onEverySecond } = _converse.exports;
window.addEventListener('click', onUserActivity);
window.addEventListener('focus', onUserActivity);
window.addEventListener('keypress', onUserActivity);
window.addEventListener('mousemove', onUserActivity);
window.addEventListener(getUnloadEvent(), onUserActivity, { 'once': true, 'passive': true });
everySecondTrigger = setInterval(onEverySecond, 1000);
}
export function tearDown() {
const { onUserActivity } = _converse.exports;
window.removeEventListener('click', onUserActivity);
window.removeEventListener('focus', onUserActivity);
window.removeEventListener('keypress', onUserActivity);
window.removeEventListener('mousemove', onUserActivity);
window.removeEventListener(getUnloadEvent(), onUserActivity);
if (everySecondTrigger) {
clearInterval(everySecondTrigger);
everySecondTrigger = null;
}
}