converse.js
Version:
Browser based XMPP chat client
236 lines (206 loc) • 7.8 kB
JavaScript
import { api, converse, log } from '@converse/headless';
import 'shared/autocomplete/index.js';
import tplAdhoc from './templates/ad-hoc.js';
import { CustomElement } from 'shared/components/element.js';
import { __ } from 'i18n';
import { getNameAndValue } from 'utils/html.js';
const { Strophe, sizzle } = converse.env;
export default class AdHocCommands extends CustomElement {
/**
* @typedef {import('@converse/headless/types/plugins/adhoc/types').AdHocCommandAction} AdHocCommandAction
* @typedef {import('./types').AdHocCommandUIProps} AdHocCommandUIProps
*/
static get properties() {
return {
alert: { type: String },
alert_type: { type: String },
commands: { type: Array },
fetching: { type: Boolean },
showform: { type: String },
view: { type: String },
};
}
constructor() {
super();
this.view = 'choose-service';
this.fetching = false;
this.showform = '';
this.commands = /** @type {AdHocCommandUIProps[]} */ ([]);
}
render() {
return tplAdhoc(this);
}
/**
* @param {SubmitEvent} ev
*/
async fetchCommands(ev) {
ev.preventDefault();
if (!(ev.target instanceof HTMLFormElement)) {
this.alert_type = 'danger';
this.alert = 'Form could not be submitted';
return;
}
this.fetching = true;
delete this.alert_type;
delete this.alert;
const form_data = new FormData(ev.target);
const jid = /** @type {string} */ (form_data.get('jid')).trim();
let supported;
try {
supported = await api.disco.supports(Strophe.NS.ADHOC, jid);
} catch (e) {
log.error(e);
} finally {
this.fetching = false;
}
if (supported) {
try {
this.commands = /** @type {AdHocCommandUIProps[]} */ (await api.adhoc.getCommands(jid));
this.view = 'list-commands';
} catch (e) {
log.error(e);
this.alert_type = 'danger';
this.alert = __('Sorry, an error occurred while looking for commands on that entity.');
this.commands = /** @type {AdHocCommandUIProps[]} */ ([]);
log.error(e);
return;
}
} else {
this.commands = [];
this.alert_type = 'danger';
this.alert = __("The specified entity doesn't support ad-hoc commands");
}
}
/**
* @param {MouseEvent} ev
*/
async toggleCommandForm(ev) {
ev.preventDefault();
const node = /** @type {Element} */ (ev.target).getAttribute('data-command-node');
const cmd = this.commands.filter((c) => c.node === node)[0];
const { jid } = cmd;
if (this.showform === node) {
this.showform = '';
this.requestUpdate();
} else {
try {
const xform = await api.adhoc.fetchCommandForm(jid, node);
Object.assign(cmd, xform);
} catch (e) {
if (e === null) {
log.error(`Error: timeout while trying to execute command for ${jid}`);
} else {
log.error(`Error while trying to execute command for ${jid}`);
log.error(e);
}
cmd.alert = __('An error occurred while trying to fetch the command form');
cmd.alert_type = 'danger';
}
this.showform = node;
}
}
/**
* @param {MouseEvent} ev
*/
executeAction(ev) {
ev.preventDefault();
const form = /** @type {HTMLFormElement} */ (ev.target);
const action = form.getAttribute('data-action');
if (['execute', 'next', 'prev', 'complete'].includes(action)) {
this.runCommand(form.form, /** @type {AdHocCommandAction} */ (action));
} else {
log.error(`Unknown action: ${action}`);
}
}
/**
* @param {AdHocCommandUIProps} cmd
*/
clearCommand(cmd) {
delete cmd.alert;
delete cmd.instructions;
delete cmd.sessionid;
delete cmd.alert_type;
delete cmd.status;
cmd.fields = [];
cmd.actions = [];
this.showform = '';
}
/**
* @param {HTMLFormElement} form
* @param {AdHocCommandAction} action
*/
async runCommand(form, action) {
const form_data = new FormData(form);
const jid = /** @type {string} */ (form_data.get('command_jid')).trim();
const node = /** @type {string} */ (form_data.get('command_node')).trim();
const cmd = this.commands.filter((c) => c.node === node)[0];
delete cmd.alert;
this.requestUpdate();
const inputs =
action === 'prev'
? []
: sizzle(':input:not([type=button]):not([type=submit])', form)
.filter(
/** @param {HTMLInputElement} i */
(i) => !['command_jid', 'command_node'].includes(i.getAttribute('name'))
)
.map(getNameAndValue)
.filter(/** @param {unknown} [n] */ (n) => n);
const response = await api.adhoc.runCommand(jid, cmd.sessionid, cmd.node, action, inputs);
const { fields, status, note, instructions, actions } = response;
if (status === 'error') {
cmd.status = status;
cmd.alert_type = 'danger';
cmd.alert = __(
'Sorry, an error occurred while trying to execute the command. See the developer console for details'
);
return this.requestUpdate();
}
if (status === 'executing') {
Object.assign(cmd, { fields, instructions, actions, status });
cmd.alert = __('Executing');
cmd.alert_type = 'primary';
} else if (status === 'completed') {
this.alert_type = 'primary';
this.alert = __('Completed');
this.note = note;
this.clearCommand(cmd);
} else {
log.error(`Unexpected status for ad-hoc command: ${status}`);
cmd.alert = __('Completed');
cmd.alert_type = 'primary';
}
this.requestUpdate();
}
/**
* @param {MouseEvent} ev
*/
async cancel(ev) {
ev.preventDefault();
this.showform = '';
this.requestUpdate();
const form_data = new FormData(/** @type {HTMLFormElement} */ (ev.target).form);
const jid = /** @type {string} */ (form_data.get('command_jid')).trim();
const node = /** @type {string} */ (form_data.get('command_node')).trim();
const cmd = this.commands.filter((c) => c.node === node)[0];
delete cmd.alert;
this.requestUpdate();
const { status } = await api.adhoc.runCommand(jid, cmd.sessionid, cmd.node, 'cancel', []);
if (status === 'error') {
cmd.alert_type = 'danger';
cmd.alert = __(
'An error occurred while trying to cancel the command. See the developer console for details'
);
} else if (status === 'canceled') {
this.alert_type = '';
this.alert = '';
this.clearCommand(cmd);
} else {
log.error(`Unexpected status for ad-hoc command: ${status}`);
cmd.alert = __('Error: unexpected result');
cmd.alert_type = 'danger';
}
this.requestUpdate();
}
}
api.elements.define('converse-adhoc-commands', AdHocCommands);