lila/ui/analyse/src/study/studyForm.ts

304 lines
8.5 KiB
TypeScript

import { h, VNode } from 'snabbdom';
import { snabModal } from 'common/modal';
import { prop, Prop } from 'common';
import { bind, bindSubmit, emptyRedButton } from '../util';
import { StudyData } from './interfaces';
import { Redraw, MaybeVNodes } from '../interfaces';
import RelayCtrl from './relay/relayCtrl';
export interface StudyFormCtrl {
open: Prop<boolean>;
openIfNew(): void;
save(data: FormData, isNew: boolean): void;
getData(): StudyData;
isNew(): boolean;
trans: Trans;
redraw: Redraw;
relay?: RelayCtrl;
}
export interface FormData {
name: string;
visibility: string;
computer: string;
explorer: string;
cloneable: string;
chat: string;
sticky: 'true' | 'false';
description: 'true' | 'false';
}
interface Select {
key: string;
name: string;
choices: Choice[];
selected: string;
}
type Choice = [string, string];
function select(s: Select): MaybeVNodes {
return [
h(
'label.form-label',
{
attrs: { for: 'study-' + s.key },
},
s.name
),
h(
`select#study-${s.key}.form-control`,
s.choices.map(function (o) {
return h(
'option',
{
attrs: {
value: o[0],
selected: s.selected === o[0],
},
},
o[1]
);
})
),
];
}
export function ctrl(
save: (data: FormData, isNew: boolean) => void,
getData: () => StudyData,
trans: Trans,
redraw: Redraw,
relay?: RelayCtrl
): StudyFormCtrl {
const initAt = Date.now();
function isNew(): boolean {
const d = getData();
return d.from === 'scratch' && !!d.isNew && Date.now() - initAt < 9000;
}
const open = prop(false);
return {
open,
openIfNew() {
if (isNew()) open(true);
},
save(data: FormData, isNew: boolean) {
save(data, isNew);
open(false);
},
getData,
isNew,
trans,
redraw,
relay,
};
}
export function view(ctrl: StudyFormCtrl): VNode {
const data = ctrl.getData();
const isNew = ctrl.isNew();
const updateName = function (vnode: VNode, isUpdate: boolean) {
const el = vnode.elm as HTMLInputElement;
if (!isUpdate && !el.value) {
el.value = data.name;
if (isNew) el.select();
el.focus();
}
};
const userSelectionChoices: Choice[] = [
['nobody', ctrl.trans.noarg('nobody')],
['owner', ctrl.trans.noarg('onlyMe')],
['contributor', ctrl.trans.noarg('contributors')],
['member', ctrl.trans.noarg('members')],
['everyone', ctrl.trans.noarg('everyone')],
];
return snabModal({
class: 'study-edit',
onClose() {
ctrl.open(false);
ctrl.redraw();
},
content: [
h('h2', ctrl.trans.noarg(ctrl.relay ? 'configureLiveBroadcast' : isNew ? 'createStudy' : 'editStudy')),
h(
'form.form3',
{
hook: bindSubmit(e => {
const getVal = (name: string): string => {
const el = (e.target as HTMLElement).querySelector('#study-' + name) as HTMLInputElement;
if (el) return el.value;
else throw `Missing form input: ${name}`;
};
ctrl.save(
{
name: getVal('name'),
visibility: getVal('visibility'),
computer: getVal('computer'),
explorer: getVal('explorer'),
cloneable: getVal('cloneable'),
chat: getVal('chat'),
sticky: getVal('sticky') as 'true' | 'false',
description: getVal('description') as 'true' | 'false',
},
isNew
);
}, ctrl.redraw),
},
[
h('div.form-group' + (ctrl.relay ? '.none' : ''), [
h('label.form-label', { attrs: { for: 'study-name' } }, ctrl.trans.noarg('name')),
h('input#study-name.form-control', {
attrs: {
minlength: 3,
maxlength: 100,
},
hook: {
insert: vnode => updateName(vnode, false),
postpatch: (_, vnode) => updateName(vnode, true),
},
}),
]),
h('div.form-split', [
h(
'div.form-group.form-half',
select({
key: 'visibility',
name: ctrl.trans.noarg('visibility'),
choices: [
['public', ctrl.trans.noarg('public')],
['unlisted', ctrl.trans.noarg('unlisted')],
['private', ctrl.trans.noarg('inviteOnly')],
],
selected: data.visibility,
})
),
h(
'div.form-group.form-half',
select({
key: 'cloneable',
name: ctrl.trans.noarg('allowCloning'),
choices: userSelectionChoices,
selected: data.settings.cloneable,
})
),
]),
h('div.form-split', [
h(
'div.form-group.form-half',
select({
key: 'computer',
name: ctrl.trans.noarg('computerAnalysis'),
choices: userSelectionChoices.map(c => [c[0], ctrl.trans.noarg(c[1])]),
selected: data.settings.computer,
})
),
h(
'div.form-group.form-half',
select({
key: 'explorer',
name: ctrl.trans.noarg('openingExplorer'),
choices: userSelectionChoices,
selected: data.settings.explorer,
})
),
]),
h('div.form-split', [
h(
'div.form-group.form-half',
select({
key: 'chat',
name: ctrl.trans.noarg('chat'),
choices: userSelectionChoices,
selected: data.settings.chat,
})
),
h(
'div.form-group.form-half',
select({
key: 'sticky',
name: ctrl.trans.noarg('enableSync'),
choices: [
['true', ctrl.trans.noarg('yesKeepEveryoneOnTheSamePosition')],
['false', ctrl.trans.noarg('noLetPeopleBrowseFreely')],
],
selected: '' + data.settings.sticky,
})
),
]),
h(
'div.form-group.form-half',
select({
key: 'description',
name: ctrl.trans.noarg('pinnedStudyComment'),
choices: [
['false', ctrl.trans.noarg('noPinnedComment')],
['true', ctrl.trans.noarg('rightUnderTheBoard')],
],
selected: '' + data.settings.description,
})
),
h(`div.form-actions${ctrl.relay ? '' : '.single'}`, [
...(ctrl.relay
? [
h(
'a.text',
{
attrs: { 'data-icon': '', href: `/broadcast/${ctrl.relay.data.tour.id}/edit` },
},
'Tournament settings'
),
h(
'a.text',
{
attrs: { 'data-icon': '', href: `/broadcast/round/${data.id}/edit` },
},
'Round settings'
),
]
: []),
h(
'button.button',
{
attrs: { type: 'submit' },
},
ctrl.trans.noarg(isNew ? 'start' : 'save')
),
]),
]
),
h('div.destructive', [
isNew
? null
: h(
'form',
{
attrs: {
action: '/study/' + data.id + '/clear-chat',
method: 'post',
},
hook: bind('submit', _ => {
return confirm(ctrl.trans.noarg('deleteTheStudyChatHistory'));
}),
},
[h(emptyRedButton, ctrl.trans.noarg('clearChat'))]
),
h(
'form',
{
attrs: {
action: '/study/' + data.id + '/delete',
method: 'post',
},
hook: bind('submit', _ => {
return isNew || confirm(ctrl.trans.noarg('deleteTheEntireStudy'));
}),
},
[h(emptyRedButton, ctrl.trans.noarg(isNew ? 'cancel' : 'deleteStudy'))]
),
]),
],
});
}