summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/video-app-settings.js
blob: 5c79bc5f640adf791a5312569caa8ce9b317e582 (plain) (blame)
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import { html, useState, useEffect } from './vendor/htm-preact.js';
import { t, getLocale, LOCALES } from './i18n.js';
import { ToggleSwitch, useSaver } from './settings-ui.js';
import { FolderPickerField } from './folder-tree.js';

// MeshBay's own locale codes (i18n.js LOCALES) to the language tag TMDB
// expects. Duplicated from nothing: this is the only place it lives now that
// the TMDB fields moved out of the monolithic settings page.
const TMDB_LANGUAGE_BY_LOCALE = {
  en: 'en-US', fr: 'fr-FR', es: 'es-ES', 'pt-BR': 'pt-BR', 'zh-CN': 'zh-CN',
  ja: 'ja-JP', de: 'de-DE', it: 'it-IT', nl: 'nl-NL', pl: 'pl-PL',
};

/**
 * The Videos app's operator settings: which folders it works over, and the
 * TMDB lookups it makes.
 *
 * Several folders now, not one. A film library is as likely to be two drives
 * as one, and the single-root model made the second one invisible — the
 * operator's only recourse was to point Videos at a parent containing both,
 * which pulls in everything else under it too.
 *
 * The TMDB parts are two independent settings that happen to sit together:
 * the on/off switch is per group, while the API key and the query language
 * are node-wide, because they are one operator's credential and one shared
 * cache (docs/mediacenter.md §5.5). They save separately for that reason.
 */
function VideoSettings({ roots, dirs, settings, saveDirectories, transport, signFn }) {
  const { busy, msg, run } = useSaver();
  const [directories, setDirectories] = useState(settings.videoDirectories || []);
  const [tmdbEnabled, setTmdbEnabled] = useState(settings.tmdbEnabled !== false);
  const [token, setToken] = useState('');
  const [language, setLanguage] = useState(
    settings.tmdbLanguage || TMDB_LANGUAGE_BY_LOCALE[getLocale()] || 'en-US');

  useEffect(() => {
    setDirectories(settings.videoDirectories || []);
  }, [settings.videoDirectories]);
  useEffect(() => {
    setTmdbEnabled(settings.tmdbEnabled !== false);
  }, [settings.tmdbEnabled]);
  useEffect(() => {
    if (settings.tmdbLanguage) setLanguage(settings.tmdbLanguage);
  }, [settings.tmdbLanguage]);

  const current = settings.videoDirectories || [];
  const dirsDirty = directories.length !== current.length
    || directories.some((d, i) => d !== current[i]);

  return html`
    <div class="app-settings">
      <${FolderPickerField}
        label=${t('settings_app.video_directories_label')}
        hint=${t('settings_app.video_directories_hint')}
        roots=${roots} dirs=${dirs} mode="multi"
        value=${directories} disabled=${busy}
        onChange=${setDirectories} />

      <button class="app-save" disabled=${busy || !dirsDirty}
        onClick=${() => run(() => saveDirectories(directories))}>
        ${busy ? t('settings_app.saving') : t('settings_app.save')}
      </button>

      <h4 class="app-settings-sub">${t('settings_node.tmdb_title')}</h4>
      <p class="settings-hint">${t('settings_node.tmdb_hint')}</p>

      <div class="settings-row">
        <${ToggleSwitch} checked=${tmdbEnabled} disabled=${busy}
          onChange=${(v) => { setTmdbEnabled(v);
                              run(() => transport.setTmdbEnabled(v, signFn)); }}
          label=${tmdbEnabled ? t('settings_node.tmdb_enabled')
                              : t('settings_node.tmdb_disabled')} />
      </div>

      <div class="settings-row">
        <label class="settings-label">
          ${t('settings_node.tmdb_token_label')}
          <input type="password" value=${token} disabled=${busy}
            placeholder=${t('settings_node.tmdb_token_placeholder')}
            onInput=${(e) => setToken(e.target.value)} />
        </label>
        ${/* No "optional", and no mention of a shipped default. A key that
              works without one is a key somebody else is paying the rate
              limit for, and the operator should know they are meant to have
              their own. */''}
        <p class="settings-hint">
          ${settings.tmdbTokenCustomized
            ? t('settings_node.tmdb_token_customized')
            : t('settings_app.tmdb_token_prompt')}
          ${' '}
          <a href="https://www.themoviedb.org/settings/api" target="_blank"
             rel="noopener noreferrer">${t('settings_app.tmdb_token_link')}</a>
        </p>
      </div>

      <div class="settings-row">
        <label class="settings-label">
          ${t('settings_node.tmdb_language_label')}
          <select value=${language} disabled=${busy}
            onChange=${(e) => setLanguage(e.target.value)}>
            ${LOCALES.map((l) => html`
              <option key=${l.code} value=${TMDB_LANGUAGE_BY_LOCALE[l.code]}>
                ${l.name}
              </option>`)}
          </select>
        </label>
        <p class="settings-hint">${t('settings_node.tmdb_language_hint')}</p>
      </div>

      ${/* The same control as the one above it. Two Save buttons in one pane
            that do not look alike is worse than either looking wrong. This one
            is never inert: the language always has a value to send, and an
            empty token means "leave the stored one alone", not "no change". */''}
      <button class="app-save" disabled=${busy}
        onClick=${() => run(async () => {
          // `undefined` for the token means "leave the stored one alone",
          // which is not the same as `''` — that clears it. The input starts
          // empty on every render because a secret is not read back, so
          // sending it as a value would wipe the key every time the language
          // was changed.
          await transport.setTmdbConfig(token || undefined, language, signFn);
          setToken('');
        })}>
        ${busy ? t('settings_app.saving') : t('settings_node.tmdb_save')}
      </button>
      ${msg && html`<p class="settings-hint">${msg}</p>`}
    </div>
  `;
}

export { VideoSettings, TMDB_LANGUAGE_BY_LOCALE };