1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
import { html, useState, useEffect } from './vendor/htm-preact.js';
import { t } from './i18n.js';
import { ToggleSwitch, useSaver } from './settings-ui.js';
import { FolderPickerField } from './folder-tree.js';
/**
* Chat's operator settings.
*
* Every app's settings pane takes the same props (see `apps.js`): the group's
* roots and known folders, the node's current answers, a `saveDirectories`
* bound to this app, and the transport for anything the app alone needs. It
* owns its drafts and its own busy state, and the page renders it without
* naming it.
*
* The directory here is unlike every other app's. Videos, Music and Photos
* point at folders they *read*; this is where attachments get *written*, so it
* has to be on a read-write root. The picker greys out the rest rather than
* letting the node's refusal arrive after the fact.
*/
function ChatSettings({ roots, dirs, settings, saveDirectories, transport, signFn }) {
const { busy, msg, run } = useSaver();
const [directory, setDirectory] = useState(settings.chatDirectory || '');
const [linkPreview, setLinkPreview] = useState(settings.chatLinkPreview !== false);
// Re-seeded from the node's answer: another operator may be editing the
// same group, and their change arrives here as a prop.
useEffect(() => { setDirectory(settings.chatDirectory || ''); },
[settings.chatDirectory]);
useEffect(() => { setLinkPreview(settings.chatLinkPreview !== false); },
[settings.chatLinkPreview]);
const noWritable = !(roots || []).some((r) => r.writable);
const dirty = directory !== (settings.chatDirectory || '');
return html`
<div class="app-settings">
${noWritable && html`
<p class="settings-hint">${t('settings_app.chat_no_writable_root')}</p>`}
<${FolderPickerField}
label=${t('settings_app.chat_directory_label')}
hint=${t('settings_app.chat_directory_hint')}
roots=${roots} dirs=${dirs}
mode="single" requireWritable=${true}
value=${directory} disabled=${busy || noWritable}
onChange=${setDirectory} />
<button class="app-save" disabled=${busy || !dirty}
onClick=${() => run(() => transport.setChatDirectory(directory, signFn))}>
${busy ? t('settings_app.saving') : t('settings_app.save')}
</button>
<div class="settings-row" style="margin-top:12px">
<${ToggleSwitch} checked=${linkPreview} disabled=${busy}
onChange=${(v) => {
setLinkPreview(v);
run(() => transport.setChatLinkPreview(v, signFn));
}}
label=${t('settings_app.chat_link_preview_label')} />
<p class="settings-hint">${t('settings_app.chat_link_preview_hint')}</p>
</div>
${msg && html`<p class="settings-hint">${msg}</p>`}
</div>
`;
}
export { ChatSettings };
|