cordis/client/webapp/elements/overlay-modify-channel.ts
2021-11-01 23:29:24 -05:00

158 lines
7.3 KiB
TypeScript

import * as electronRemote from '@electron/remote';
const electronConsole = electronRemote.getGlobal('console') as Console;
import Logger from '../../../logger/logger';
const LOG = Logger.create(__filename, electronConsole);
import { Channel } from '../data-types';
import ClientController from '../client-controller.js';
import Globals from '../globals.js';
import BaseElements from './require/base-elements.js';
import IState from './require/elements-state.js';
import ElementsUtil from './require/elements-util.js';
import { $, $$, $$$, $$$$ } from './require/q-module';
export default function createModifyChannelOverlay(state: IState, server: ClientController, channel: Channel): HTMLElement {
const { document } = state;
$.setDocument(document);
// See also overlay-create-channel
let element = BaseElements.createOverlay(document, { class: 'content submit-dialog modify-channel', content: [
{ class: 'preview channel-title', content: [
{ class: 'channel-icon', content: BaseElements.TEXT_CHANNEL_ICON },
{ class: 'channel-name', content: channel.name },
{ class: 'channel-flavor-divider' },
{ class: 'channel-flavor-text', content: channel.flavorText || '' }
] },
{ class: 'text-input channel-name', placeholder: 'channel-name', content: channel.name, contenteditable: 'plaintext-only' },
{ class: 'text-input channel-flavor-text', placeholder: 'Flavor Text (optional)', content: channel.flavorText || '', contenteditable: 'plaintext-only' },
{ class: 'lower', content: [
{ class: 'error' },
{ class: 'buttons', content: [
{ class: 'button submit', content: 'Save Changes' }
] }
] }
] });
let newName = channel.name;
let newFlavorText = channel.flavorText;
function updatePreview() {
newName = $$$(element, '.text-input.channel-name').innerText;
newFlavorText = $$$(element, '.text-input.channel-flavor-text').innerText;
$$$(element, '.channel-title .channel-name').innerText = newName;
$$$(element, '.channel-title .channel-flavor-text').innerText = newFlavorText;
if (newFlavorText != '') {
$$$(element, '.channel-title .channel-flavor-divider').style.visibility = 'visible';
} else {
$$$(element, '.channel-title .channel-flavor-divider').style.visibility = 'hidden';
}
}
updatePreview();
let submitting = false;
async function submit() {
if (submitting) return;
submitting = true;
$$$(element, '.error').innerText = '';
$$$(element, '.button.submit').innerText = 'Submitting...';
$$$(element, '.text-input.channel-name').removeAttribute('contenteditable');
$$$(element, '.text-input.channel-flavor-text').removeAttribute('contenteditable');
let success = false;
if (newName == channel.name && (newFlavorText || '') == (channel.flavorText || '')) {
success = true; // nothing changed
} else if (newName.length == 0) {
LOG.warn('attempted to set empty channel name');
$$$(element, '.error').innerText = 'Channel name cannot be empty';
$$$(element, '.button.submit').innerText = 'Try Again';
await ElementsUtil.shakeElement($$$(element, '.button.submit'), 400);
} else if (newName.length > Globals.MAX_CHANNEL_NAME_LENGTH) {
LOG.warn('attempted to set too long channel name');
$$$(element, '.error').innerText = 'Channel name is too long. ' + newName.length + ' > ' + Globals.MAX_CHANNEL_NAME_LENGTH;
$$$(element, '.button.submit').innerText = 'Try Again';
await ElementsUtil.shakeElement($$$(element, '.button.submit'), 400);
} else if (!(/^[A-Za-z0-9-]+$/.exec(newName))) {
LOG.warn('attempted to set channel name with illegal characters');
$$$(element, '.error').innerText = 'Please use only [A-Za-z0-9-]+ in channel name';
$$$(element, '.button.submit').innerText = 'Try Again';
await ElementsUtil.shakeElement($$$(element, '.button.submit'), 400);
} else if (newFlavorText != null && newFlavorText.length > Globals.MAX_CHANNEL_FLAVOR_TEXT_LENGTH) {
LOG.warn('attempted to set too long flavor text');
$$$(element, '.error').innerText = 'Flavor text is too long. ' + newName.length + ' > ' + Globals.MAX_CHANNEL_NAME_LENGTH;
$$$(element, '.button.submit').innerText = 'Try Again';
await ElementsUtil.shakeElement($$$(element, '.button.submit'), 400);
} else {
if (newFlavorText != null && newFlavorText.length == 0) {
newFlavorText = null;
}
try {
await server.updateChannel(channel.id, newName, newFlavorText);
success = true;
} catch (e) {
LOG.error('error updating channel', e);
$$$(element, '.error').innerText = 'Error updating channel';
$$$(element, '.button.submit').innerText = 'Try Again';
await ElementsUtil.shakeElement($$$(element, '.button.submit'), 400);
}
}
if (success) {
element.removeSelf();
}
$$$(element, '.text-input.channel-name').setAttribute('contenteditable', 'plaintext-only');
$$$(element, '.text-input.channel-flavor-text').setAttribute('contenteditable', 'plaintext-only');
submitting = false;
}
let textInputs = $$$$(element, '.text-input');
for (let textInput of textInputs) {
textInput.addEventListener('input', () => {
updatePreview();
});
}
$$$(element, '.text-input.channel-name').addEventListener('keydown', async (e) => {
if (e.key == 'Backspace' || e.key == 'Escape' || e.key == 'F4') {
// these keys are good
} else if (e.key == 'Tab') {
// have to hard-code this one because otherwise, it just picks the beginning of the next input
e.preventDefault();
e.stopPropagation();
$$$(element, '.text-input.channel-flavor-text').focus();
ElementsUtil.setCursorToEnd($$$(element, '.text-input.channel-flavor-text'));
} else if (e.key == 'Enter') {
e.preventDefault();
e.stopPropagation();
await submit();
} else if (!/^[A-Za-z0-9-]$/.exec(e.key)) {
e.preventDefault();
e.stopPropagation();
}
});
$$$(element, '.text-input.channel-flavor-text').addEventListener('keydown', async (e) => {
if (e.key == 'Tab' && e.shiftKey) {
// have to hard-code this one because otherwise, it just picks the beginning of the next input
e.preventDefault();
e.stopPropagation();
$$$(element, '.text-input.channel-name').focus();
ElementsUtil.setCursorToEnd($$$(element, '.text-input.channel-name'));
} else if (e.key == 'Enter') {
e.preventDefault();
e.stopPropagation();
await submit();
}
});
$$$(element, '.button.submit').addEventListener('click', async () => {
await submit();
});
return element;
}