145 lines
4.2 KiB
TypeScript
145 lines
4.2 KiB
TypeScript
import {
|
|
createContext,
|
|
useCallback,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
} from 'react'
|
|
|
|
import {type AppLanguage} from '#/locale/languages'
|
|
import * as persisted from '#/state/persisted'
|
|
import {AnalyticsContext, utils} from '#/analytics'
|
|
|
|
type SetStateCb = (
|
|
s: persisted.Schema['languagePrefs'],
|
|
) => persisted.Schema['languagePrefs']
|
|
type StateContext = persisted.Schema['languagePrefs']
|
|
type ApiContext = {
|
|
setPrimaryLanguage: (code2: string) => void
|
|
setPostLanguage: (commaSeparatedLangCodes: string) => void
|
|
setContentLanguages: (code2s: string[]) => void
|
|
savePostLanguageToHistory: () => void
|
|
setAppLanguage: (code2: AppLanguage) => void
|
|
}
|
|
|
|
const stateContext = createContext<StateContext>(
|
|
persisted.defaults.languagePrefs,
|
|
)
|
|
stateContext.displayName = 'LanguagePrefsStateContext'
|
|
const apiContext = createContext<ApiContext>({
|
|
setPrimaryLanguage: (_: string) => {},
|
|
setPostLanguage: (_: string) => {},
|
|
setContentLanguages: (_: string[]) => {},
|
|
savePostLanguageToHistory: () => {},
|
|
setAppLanguage: (_: AppLanguage) => {},
|
|
})
|
|
apiContext.displayName = 'LanguagePrefsApiContext'
|
|
|
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
|
const [state, setState] = useState(() => persisted.get('languagePrefs'))
|
|
|
|
const setStateWrapped = useCallback(
|
|
(fn: SetStateCb) => {
|
|
const s = fn(persisted.get('languagePrefs'))
|
|
setState(s)
|
|
persisted.write('languagePrefs', s)
|
|
},
|
|
[setState],
|
|
)
|
|
|
|
useEffect(() => {
|
|
return persisted.onUpdate('languagePrefs', nextLanguagePrefs => {
|
|
setState(nextLanguagePrefs)
|
|
})
|
|
}, [setStateWrapped])
|
|
|
|
const api = useMemo(
|
|
() => ({
|
|
setPrimaryLanguage(code2: string) {
|
|
setStateWrapped(s => ({...s, primaryLanguage: code2}))
|
|
},
|
|
setPostLanguage(commaSeparatedLangCodes: string) {
|
|
setStateWrapped(s => ({...s, postLanguage: commaSeparatedLangCodes}))
|
|
},
|
|
setContentLanguages(code2s: string[]) {
|
|
setStateWrapped(s => ({...s, contentLanguages: code2s}))
|
|
},
|
|
/**
|
|
* 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: AppLanguage) {
|
|
setStateWrapped(s => ({...s, appLanguage: code2}))
|
|
},
|
|
}),
|
|
[setStateWrapped],
|
|
)
|
|
|
|
return (
|
|
<stateContext.Provider value={state}>
|
|
<apiContext.Provider value={api}>
|
|
<AnalyticsContext
|
|
metadata={utils.useMeta({
|
|
preferences: {
|
|
appLanguage: state.appLanguage,
|
|
contentLanguages: state.contentLanguages,
|
|
},
|
|
})}>
|
|
{children}
|
|
</AnalyticsContext>
|
|
</apiContext.Provider>
|
|
</stateContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useLanguagePrefs() {
|
|
return useContext(stateContext)
|
|
}
|
|
|
|
export function useLanguagePrefsApi() {
|
|
return useContext(apiContext)
|
|
}
|
|
|
|
export function getContentLanguages() {
|
|
return persisted.get('languagePrefs').contentLanguages
|
|
}
|
|
|
|
/**
|
|
* Be careful with this. It's used for the PWI home screen so that users can
|
|
* select a UI language and have it apply to the fetched Discover feed.
|
|
*
|
|
* We only support BCP-47 two-letter codes here, hence the split.
|
|
*/
|
|
export function getAppLanguageAsContentLanguage() {
|
|
return persisted.get('languagePrefs').appLanguage.split('-')[0]
|
|
}
|
|
|
|
export function toPostLanguages(postLanguage: string): string[] {
|
|
// filter out empty strings if exist
|
|
return postLanguage.split(',').filter(Boolean)
|
|
}
|
|
|
|
export function fromPostLanguages(languages: string[]): string {
|
|
return languages.filter(Boolean).join(',')
|
|
}
|
|
|
|
export function hasPostLanguage(postLanguage: string, code2: string): boolean {
|
|
return toPostLanguages(postLanguage).includes(code2)
|
|
}
|