@jsxc/jsxc
Version:
Real-time XMPP chat application with video calls, file transfer and encrypted communication
262 lines (196 loc) • 8.13 kB
text/typescript
import Dialog from '../Dialog';
import Form from '../../connection/Form';
import Log from '../../util/Log';
import openContactDialog from './contact';
import Translation from '@util/Translation';
import Client from '../../Client';
import Account from '../../Account';
import JID from '../../JID';
import { IJID } from '@src/JID.interface';
import FormField from '@connection/FormField';
export const CANCELED = 'canceled';
const possibleSearchFields = ['first', 'last', 'nick', 'email'];
let contactSearchTemplate = require('../../../template/contactsearch.hbs');
export default function () {
let content = contactSearchTemplate();
let dialog = new Dialog(content);
dialog.open();
let accounts = Client.getAccountManager().getAccounts();
accounts.forEach(account => {
let accountoption = $('<option>').text(account.getJID().bare).val(account.getUid());
dialog.getDom().find('select[name="account"]').append(accountoption);
});
dialog
.getDom()
.find('select[name="account"]')
.on('change', ev => {
let uid = $(ev.target).val().toString();
loadForm(Client.getAccountManager().getAccount(uid), dialog);
});
loadForm(accounts[0], dialog);
}
function loadForm(account: Account, dialog: Dialog) {
let searchService = account.getConnection().getSearchService();
getSearchService(account).then(async services => {
if (!services || services.length === 0) {
dialog.getDom().find('.jsxc-content').text(Translation.t('not_available'));
return;
}
let searchFormElement = await searchService.getSearchForm(services[0]);
let formElement = generateForm(searchFormElement);
dialog.getDom().find('.jsxc-content').empty().append(formElement);
dialog.getDom().find('.jsxc-results').empty();
formElement.find('.jsxc-js-close').on('click', () => dialog.close());
formElement.on('submit', ev => {
ev.preventDefault();
formElement.find('input, button').prop('disabled', true);
let form;
if (formElement.hasClass('jsxc-simple-form')) {
form = {};
possibleSearchFields.forEach(name => {
form[name] = formElement.find(`input[name="${name}"]`).val();
});
} else {
form = Form.fromHTML(formElement.get(0));
}
searchService
.executeSearchForm(services[0], form)
.then(resultStanza => {
appendSearchResults(resultStanza, dialog);
})
.catch(err => {})
.then(() => {
formElement.find('input, button').prop('disabled', false);
});
});
});
}
async function getSearchService(account: Account): Promise<IJID[]> {
let connection = account.getConnection();
let ownJid = connection.getJID();
let serverJid = new JID('', ownJid.domain, '');
let discoInfoRepository = account.getDiscoInfoRepository();
return connection
.getDiscoService()
.getDiscoItems(serverJid)
.then(stanza => {
let promises = $(stanza)
.find('item')
.map((index, element) => {
let jid = new JID('', $(element).attr('jid'), '');
return discoInfoRepository
.requestDiscoInfo(jid)
.then(discoInfo => {
let hasSearch = discoInfo.hasFeature('jabber:iq:search');
let isMUC = discoInfo.hasFeature('http://jabber.org/protocol/muc');
return hasSearch && !isMUC ? jid : undefined;
})
.catch(stanza => {
const from = $(stanza).attr('from') || '';
Log.info(`Ignore ${from} as Search provider, because could not load disco info.`);
return undefined;
});
})
.get();
return Promise.all(promises).then(results => {
return results.filter(jid => typeof jid !== 'undefined');
});
});
}
function generateForm(stanza: Element) {
let formElement =
$(stanza).find('x[xmlns="jabber:x:data"]').length > 0
? Form.fromXML(stanza).toHTML()
: generateSimpleForm(stanza);
formElement.append(`<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button class="jsxc-button jsxc-button--default jsxc-js-close" type="button">${Translation.t('Close')}</button>
<button class="jsxc-button jsxc-button--primary" type="submit">${Translation.t('Search')}</button>
</div>
</div>`);
return formElement;
}
function generateSimpleForm(element: Element) {
let formElement = $('form');
formElement.attr('autocomplete', 'off');
formElement.addClass('form-horizontal jsxc-simple-form');
let queryElement = $(element).find('query[xmlns="jabber:iq:search"]');
let instructionsElement = queryElement.find('>instructions');
if (instructionsElement.length > 0) {
$('<p>').text(instructionsElement.text()).appendTo(formElement);
}
possibleSearchFields.forEach(field => {
let fieldElement = queryElement.find('>' + field);
if (fieldElement.length > 0) {
let formField = new FormField({ name: field });
formElement.append(formField.toHTML());
}
});
return formElement;
}
function appendSearchResults(resultStanza: Element, dialog: Dialog) {
let table: JQuery;
let items = $(resultStanza).find('query>item');
if ($(resultStanza).find('x[xmlns="jabber:x:data"]').length > 0) {
table = Form.fromXML(resultStanza).toHTML();
} else {
table = generateTableForSimpleResult(items);
}
table.find('tr').each((index, row) => {
let cell = $('<td>');
if (index > 0) {
cell.append(`<input type="radio" name="jsxc-add-contact" value="${index - 1}" required="required"/>`);
}
$(row).prepend(cell);
});
let dom = dialog.getDom();
table.addClass('jsxc-searchresults-table');
dialog.getDom().find('.jsxc-results').empty().append($('<form>').append(table));
dom.find('.jsxc-results form').append(`<div class="form-group">
<div class="col-sm-offset-4 col-sm-8">
<button class="jsxc-button jsxc-button--primary" type="submit">${Translation.t('Add')}</button>
</div>
</div>`);
dom.find('.jsxc-results form').on('submit', function (ev) {
ev.preventDefault();
let bareJid = '';
let alias = '';
let selectedIndex = parseInt(dom.find('input[name="jsxc-add-contact"]:checked').val().toString(), 10);
if (!isNaN(selectedIndex)) {
let extendedItem = $(resultStanza).find('x[xmlns="jabber:x:data"] > item').eq(selectedIndex);
let simpleItem = $(resultStanza).find('item').eq(selectedIndex);
if (extendedItem.length === 1) {
bareJid = extendedItem.find('field[var="jid"]').text();
alias =
extendedItem.find('field[var="first"]').text() + ' ' + extendedItem.find('field[var="last"]').text();
} else if (simpleItem.length === 1) {
bareJid = simpleItem.attr('jid');
alias = simpleItem.find('first').text() + ' ' + simpleItem.find('last').text();
}
}
dialog.close();
openContactDialog(bareJid, alias);
});
dom.find('.jsxc-js-close').on('click', () => {
dialog.close();
});
}
function generateTableForSimpleResult(items: JQuery) {
let tableElement = $('<table>');
let tableHeader = $('<thead>');
let headerRow = $('<tr>');
tableElement.append(tableHeader);
tableHeader.append(headerRow);
const columnNames = ['First', 'Last', 'Nick', 'Email'];
columnNames.forEach(name => $('<th>').text(Translation.t(name)).appendTo(headerRow));
let tableBody = $('<tbody>');
tableElement.append(tableBody);
for (let item of items) {
let row = $('<tr>');
columnNames.forEach(name => {
$('<td>').text($(item).children(name.toLowerCase()).text()).appendTo(row);
});
tableBody.append(row);
}
return tableElement;
}