about summary refs log tree commit diff
path: root/src/state/preferences/languages.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/state/preferences/languages.tsx')
-rw-r--r--src/state/preferences/languages.tsx142
1 files changed, 142 insertions, 0 deletions
diff --git a/src/state/preferences/languages.tsx b/src/state/preferences/languages.tsx
new file mode 100644
index 000000000..8e779cfe5
--- /dev/null
+++ b/src/state/preferences/languages.tsx
@@ -0,0 +1,142 @@
+import React from 'react'
+import * as persisted from '#/state/persisted'
+
+type SetStateCb = (
+  s: persisted.Schema['languagePrefs'],
+) => persisted.Schema['languagePrefs']
+type StateContext = persisted.Schema['languagePrefs']
+type ApiContext = {
+  setPrimaryLanguage: (code2: string) => void
+  setPostLanguage: (commaSeparatedLangCodes: string) => void
+  toggleContentLanguage: (code2: string) => void
+  togglePostLanguage: (code2: string) => void
+  savePostLanguageToHistory: () => void
+  setAppLanguage: (code2: string) => void
+}
+
+const stateContext = React.createContext<StateContext>(
+  persisted.defaults.languagePrefs,
+)
+const apiContext = React.createContext<ApiContext>({
+  setPrimaryLanguage: (_: string) => {},
+  setPostLanguage: (_: string) => {},
+  toggleContentLanguage: (_: string) => {},
+  togglePostLanguage: (_: string) => {},
+  savePostLanguageToHistory: () => {},
+  setAppLanguage: (_: string) => {},
+})
+
+export function Provider({children}: React.PropsWithChildren<{}>) {
+  const [state, setState] = React.useState(persisted.get('languagePrefs'))
+
+  const setStateWrapped = React.useCallback(
+    (fn: SetStateCb) => {
+      const s = fn(persisted.get('languagePrefs'))
+      setState(s)
+      persisted.write('languagePrefs', s)
+    },
+    [setState],
+  )
+
+  React.useEffect(() => {
+    return persisted.onUpdate(() => {
+      setState(persisted.get('languagePrefs'))
+    })
+  }, [setStateWrapped])
+
+  const api = React.useMemo(
+    () => ({
+      setPrimaryLanguage(code2: string) {
+        setStateWrapped(s => ({...s, primaryLanguage: code2}))
+      },
+      setPostLanguage(commaSeparatedLangCodes: string) {
+        setStateWrapped(s => ({...s, postLanguage: commaSeparatedLangCodes}))
+      },
+      toggleContentLanguage(code2: string) {
+        setStateWrapped(s => {
+          const exists = s.contentLanguages.includes(code2)
+          const next = exists
+            ? s.contentLanguages.filter(lang => lang !== code2)
+            : s.contentLanguages.concat(code2)
+          return {
+            ...s,
+            contentLanguages: next,
+          }
+        })
+      },
+      togglePostLanguage(code2: string) {
+        setStateWrapped(s => {
+          const exists = hasPostLanguage(state.postLanguage, code2)
+          let next = s.postLanguage
+
+          if (exists) {
+            next = toPostLanguages(s.postLanguage)
+              .filter(lang => lang !== code2)
+              .join(',')
+          } else {
+            // sort alphabetically for deterministic comparison in context menu
+            next = toPostLanguages(s.postLanguage)
+              .concat([code2])
+              .sort((a, b) => a.localeCompare(b))
+              .join(',')
+          }
+
+          return {
+            ...s,
+            postLanguage: next,
+          }
+        })
+      },
+      /**
+       * Saves whatever language codes are currently selected into a history array,
+       * which is then used to populate the language selector menu.
+       */
+      savePostLanguageToHistory() {
+        // filter out duplicate `this.postLanguage` if exists, and prepend
+        // value to start of array
+        setStateWrapped(s => ({
+          ...s,
+          postLanguageHistory: [s.postLanguage]
+            .concat(
+              s.postLanguageHistory.filter(
+                commaSeparatedLangCodes =>
+                  commaSeparatedLangCodes !== s.postLanguage,
+              ),
+            )
+            .slice(0, 6),
+        }))
+      },
+      setAppLanguage(code2: string) {
+        setStateWrapped(s => ({...s, appLanguage: code2}))
+      },
+    }),
+    [state, setStateWrapped],
+  )
+
+  return (
+    <stateContext.Provider value={state}>
+      <apiContext.Provider value={api}>{children}</apiContext.Provider>
+    </stateContext.Provider>
+  )
+}
+
+export function useLanguagePrefs() {
+  return React.useContext(stateContext)
+}
+
+export function useLanguagePrefsApi() {
+  return React.useContext(apiContext)
+}
+
+export function getContentLanguages() {
+  return persisted.get('languagePrefs').contentLanguages
+}
+
+export function toPostLanguages(postLanguage: string): string[] {
+  // filter out empty strings if exist
+  return postLanguage.split(',').filter(Boolean)
+}
+
+export function hasPostLanguage(postLanguage: string, code2: string): boolean {
+  return toPostLanguages(postLanguage).includes(code2)
+}