@jsxc/jsxc
Version:
Real-time XMPP chat application with video calls, file transfer and encrypted communication
159 lines (120 loc) • 4.28 kB
text/typescript
import { ILinkHandler } from './LinkHandler.interface';
import { IJID } from './JID.interface';
import JID from './JID';
import Client from './Client';
import { IContact } from './Contact.interface';
import Contact from './Contact';
import showMultiUserJoinDialog from './ui/dialogs/multiUserJoin';
import Log from '@util/Log';
export default class LinkHandlerXMPP implements ILinkHandler {
private static instance: LinkHandlerXMPP;
public static get(): LinkHandlerXMPP {
if (!LinkHandlerXMPP.instance) {
LinkHandlerXMPP.instance = new LinkHandlerXMPP();
}
return LinkHandlerXMPP.instance;
}
private constructor() {}
public execute(uri: string) {
if (!uri.startsWith('xmpp:')) {
throw new Error('Uri has to start with xmpp:');
}
let payload = uri.replace(/^xmpp:/, '');
let { jid, action, params } = this.parsePayload(payload);
if (typeof this.queryActions[action] !== 'function') {
throw new Error('Unsupported action');
}
return this.queryActions[action](jid, params);
}
public detect(element: JQuery = $('body')) {
let self = this;
element.find('a[href^="xmpp:"]').each(function () {
self.processElement($(this));
});
}
private processElement(element: JQuery) {
let payload = element.attr('href').replace(/^xmpp:/, '');
let { jid, action, params } = this.parsePayload(payload);
if (typeof this.queryActions[action] === 'function') {
element.addClass('jsxc-xmpp-scheme jsxc-xmpp-scheme--' + action);
element.off('click').click(ev => {
ev.stopPropagation();
ev.preventDefault();
this.queryActions[action](jid, params);
});
//@TODO add presence info
}
}
private parsePayload(href: string) {
let jidString = href.split('?')[0];
let action: string;
let params = {};
if (!/[^"&'\\\/:<>@\s]+@[\w-_.]+/.test(jidString)) {
return {};
}
let jid = new JID(jidString);
if (href.indexOf('?') < 0) {
action = 'message';
} else {
let parts = href.substring(href.indexOf('?') + 1).split(';');
action = parts[0];
for (let i = 1; i < parts.length; i++) {
let key = parts[i].split('=')[0];
let value = parts[i].indexOf('=') > 0 ? parts[i].substring(parts[i].indexOf('=') + 1) : null;
params[decodeURIComponent(key)] = decodeURIComponent(value);
}
}
return {
jid,
action,
params,
};
}
private queryActions = {
/** xmpp:JID?message[;body=TEXT] */
message: (jid: IJID, params?: { body: string }) => {
let contact = this.searchContact(jid);
if (typeof contact === 'undefined') {
Log.info('Can not execute query action, because no account is connected');
return false;
}
if (!contact) {
let account = Client.getAccountManager().getAccount();
contact = new Contact(account, jid);
account.getContactManager().addToCache(contact);
}
contact.getChatWindowController().openProminently();
if (params?.body) {
contact.getChatWindow().appendTextToInput(params?.body);
}
return true;
},
/** xmpp:JID?join[;password=TEXT] */
join: (jid: IJID, params?: { password: string }) => {
let contact = this.searchContact(jid);
if (typeof contact === 'undefined') {
Log.info('Can not execute query action, because no account is connected');
return false;
}
if (!contact) {
showMultiUserJoinDialog(jid.domain, jid.node);
} else {
this.queryActions.message(jid);
}
return true;
},
};
private searchContact(jid: IJID): IContact {
let accounts = Client.getAccountManager().getAccounts();
if (accounts.length === 0) {
return undefined;
}
for (let account of accounts) {
let contact = account.getContact(jid);
if (contact) {
return contact;
}
}
return null;
}
}