diff options
Diffstat (limited to 'src/state')
-rw-r--r-- | src/state/models/ui/preferences.ts | 136 | ||||
-rw-r--r-- | src/state/preferences/index.tsx | 8 | ||||
-rw-r--r-- | src/state/preferences/languages.tsx | 122 |
3 files changed, 132 insertions, 134 deletions
diff --git a/src/state/models/ui/preferences.ts b/src/state/models/ui/preferences.ts index d03fa8d24..951486592 100644 --- a/src/state/models/ui/preferences.ts +++ b/src/state/models/ui/preferences.ts @@ -10,11 +10,10 @@ import {isObj, hasProp} from 'lib/type-guards' import {RootStoreModel} from '../root-store' import {ModerationOpts} from '@atproto/api' import {DEFAULT_FEEDS} from 'lib/constants' -import {deviceLocales} from 'platform/detection' import {getAge} from 'lib/strings/time' import {FeedTuner} from 'lib/api/feed-manip' -import {LANGUAGES} from '../../../locale/languages' import {logger} from '#/logger' +import {getContentLanguages} from '#/state/preferences/languages' // TEMP we need to permanently convert 'show' to 'ignore', for now we manually convert -prf export type LabelPreference = APILabelPreference | 'show' @@ -34,9 +33,6 @@ const LABEL_GROUPS = [ 'impersonation', ] const VISIBILITY_VALUES = ['ignore', 'warn', 'hide'] -const DEFAULT_LANG_CODES = (deviceLocales || []) - .concat(['en', 'ja', 'pt', 'de']) - .slice(0, 6) const THREAD_SORT_VALUES = ['oldest', 'newest', 'most-likes', 'random'] interface LegacyPreferences { @@ -62,10 +58,6 @@ export class LabelPreferencesModel { export class PreferencesModel { adultContentEnabled = false - primaryLanguage: string = deviceLocales[0] || 'en' - contentLanguages: string[] = deviceLocales || [] - postLanguage: string = deviceLocales[0] || 'en' - postLanguageHistory: string[] = DEFAULT_LANG_CODES contentLabels = new LabelPreferencesModel() savedFeeds: string[] = [] pinnedFeeds: string[] = [] @@ -103,10 +95,6 @@ export class PreferencesModel { serialize() { return { - primaryLanguage: this.primaryLanguage, - contentLanguages: this.contentLanguages, - postLanguage: this.postLanguage, - postLanguageHistory: this.postLanguageHistory, contentLabels: this.contentLabels, savedFeeds: this.savedFeeds, pinnedFeeds: this.pinnedFeeds, @@ -120,44 +108,6 @@ export class PreferencesModel { */ hydrate(v: unknown) { if (isObj(v)) { - if ( - hasProp(v, 'primaryLanguage') && - typeof v.primaryLanguage === 'string' - ) { - this.primaryLanguage = v.primaryLanguage - } else { - // default to the device languages - this.primaryLanguage = deviceLocales[0] || 'en' - } - // check if content languages in preferences exist, otherwise default to device languages - if ( - hasProp(v, 'contentLanguages') && - Array.isArray(v.contentLanguages) && - typeof v.contentLanguages.every(item => typeof item === 'string') - ) { - this.contentLanguages = v.contentLanguages - } else { - // default to the device languages - this.contentLanguages = deviceLocales - } - if (hasProp(v, 'postLanguage') && typeof v.postLanguage === 'string') { - this.postLanguage = v.postLanguage - } else { - // default to the device languages - this.postLanguage = deviceLocales[0] || 'en' - } - if ( - hasProp(v, 'postLanguageHistory') && - Array.isArray(v.postLanguageHistory) && - typeof v.postLanguageHistory.every(item => typeof item === 'string') - ) { - this.postLanguageHistory = v.postLanguageHistory - .concat(DEFAULT_LANG_CODES) - .slice(0, 6) - } else { - // default to a starter set - this.postLanguageHistory = DEFAULT_LANG_CODES - } // check if content labels in preferences exist, then hydrate if (hasProp(v, 'contentLabels') && typeof v.contentLabels === 'object') { Object.assign(this.contentLabels, v.contentLabels) @@ -262,9 +212,6 @@ export class PreferencesModel { try { runInAction(() => { this.contentLabels = new LabelPreferencesModel() - this.contentLanguages = deviceLocales - this.postLanguage = deviceLocales ? deviceLocales.join(',') : 'en' - this.postLanguageHistory = DEFAULT_LANG_CODES this.savedFeeds = [] this.pinnedFeeds = [] }) @@ -276,81 +223,6 @@ export class PreferencesModel { } } - // languages - // = - - hasContentLanguage(code2: string) { - return this.contentLanguages.includes(code2) - } - - toggleContentLanguage(code2: string) { - if (this.hasContentLanguage(code2)) { - this.contentLanguages = this.contentLanguages.filter( - lang => lang !== code2, - ) - } else { - this.contentLanguages = this.contentLanguages.concat([code2]) - } - } - - /** - * A getter that splits `this.postLanguage` into an array of strings. - * - * This was previously the main field on this model, but now we're - * concatenating lang codes to make multi-selection a little better. - */ - get postLanguages() { - // filter out empty strings if exist - return this.postLanguage.split(',').filter(Boolean) - } - - hasPostLanguage(code2: string) { - return this.postLanguages.includes(code2) - } - - togglePostLanguage(code2: string) { - if (this.hasPostLanguage(code2)) { - this.postLanguage = this.postLanguages - .filter(lang => lang !== code2) - .join(',') - } else { - // sort alphabetically for deterministic comparison in context menu - this.postLanguage = this.postLanguages - .concat([code2]) - .sort((a, b) => a.localeCompare(b)) - .join(',') - } - } - - setPostLanguage(commaSeparatedLangCodes: string) { - this.postLanguage = commaSeparatedLangCodes - } - - /** - * 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 - this.postLanguageHistory = [this.postLanguage] - .concat( - this.postLanguageHistory.filter( - commaSeparatedLangCodes => - commaSeparatedLangCodes !== this.postLanguage, - ), - ) - .slice(0, 6) - } - - getReadablePostLanguages() { - const all = this.postLanguages.map(code2 => { - const lang = LANGUAGES.find(l => l.code2 === code2) - return lang ? lang.name : code2 - }) - return all.join(', ') - } - // moderation // = @@ -599,17 +471,13 @@ export class PreferencesModel { } } - setPrimaryLanguage(lang: string) { - this.primaryLanguage = lang - } - getFeedTuners( feedType: 'home' | 'following' | 'author' | 'custom' | 'list' | 'likes', ) { if (feedType === 'custom') { return [ FeedTuner.dedupReposts, - FeedTuner.preferredLangOnly(this.contentLanguages), + FeedTuner.preferredLangOnly(getContentLanguages()), ] } if (feedType === 'list') { diff --git a/src/state/preferences/index.tsx b/src/state/preferences/index.tsx new file mode 100644 index 000000000..56c93f812 --- /dev/null +++ b/src/state/preferences/index.tsx @@ -0,0 +1,8 @@ +import React from 'react' +import {Provider as LanguagesProvider} from './languages' + +export {useLanguagePrefs, useSetLanguagePrefs} from './languages' + +export function Provider({children}: React.PropsWithChildren<{}>) { + return <LanguagesProvider>{children}</LanguagesProvider> +} diff --git a/src/state/preferences/languages.tsx b/src/state/preferences/languages.tsx new file mode 100644 index 000000000..49b63550d --- /dev/null +++ b/src/state/preferences/languages.tsx @@ -0,0 +1,122 @@ +import React from 'react' +import * as persisted from '#/state/persisted' + +type SetStateCb = ( + v: persisted.Schema['languagePrefs'], +) => persisted.Schema['languagePrefs'] +type StateContext = persisted.Schema['languagePrefs'] +type SetContext = (fn: SetStateCb) => void + +const stateContext = React.createContext<StateContext>( + persisted.defaults.languagePrefs, +) +const setContext = React.createContext<SetContext>((_: SetStateCb) => {}) + +export function Provider({children}: React.PropsWithChildren<{}>) { + const [state, setState] = React.useState(persisted.get('languagePrefs')) + + const setStateWrapped = React.useCallback( + (fn: SetStateCb) => { + const v = fn(persisted.get('languagePrefs')) + setState(v) + persisted.write('languagePrefs', v) + }, + [setState], + ) + + React.useEffect(() => { + return persisted.onUpdate(() => { + setState(persisted.get('languagePrefs')) + }) + }, [setStateWrapped]) + + return ( + <stateContext.Provider value={state}> + <setContext.Provider value={setStateWrapped}> + {children} + </setContext.Provider> + </stateContext.Provider> + ) +} + +export function useLanguagePrefs() { + return React.useContext(stateContext) +} + +export function useSetLanguagePrefs() { + return React.useContext(setContext) +} + +export function getContentLanguages() { + return persisted.get('languagePrefs').contentLanguages +} + +export function toggleContentLanguage( + state: StateContext, + setState: SetContext, + code2: string, +) { + if (state.contentLanguages.includes(code2)) { + setState(v => ({ + ...v, + contentLanguages: v.contentLanguages.filter(lang => lang !== code2), + })) + } else { + setState(v => ({ + ...v, + contentLanguages: v.contentLanguages.concat(code2), + })) + } +} + +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) +} + +export function togglePostLanguage( + state: StateContext, + setState: SetContext, + code2: string, +) { + if (hasPostLanguage(state.postLanguage, code2)) { + setState(v => ({ + ...v, + postLanguage: toPostLanguages(v.postLanguage) + .filter(lang => lang !== code2) + .join(','), + })) + } else { + // sort alphabetically for deterministic comparison in context menu + setState(v => ({ + ...v, + postLanguage: toPostLanguages(v.postLanguage) + .concat([code2]) + .sort((a, b) => a.localeCompare(b)) + .join(','), + })) + } +} + +/** + * Saves whatever language codes are currently selected into a history array, + * which is then used to populate the language selector menu. + */ +export function savePostLanguageToHistory(setState: SetContext) { + // filter out duplicate `this.postLanguage` if exists, and prepend + // value to start of array + setState(v => ({ + ...v, + postLanguageHistory: [v.postLanguage] + .concat( + v.postLanguageHistory.filter( + commaSeparatedLangCodes => commaSeparatedLangCodes !== v.postLanguage, + ), + ) + .slice(0, 6), + })) +} |