diff --git a/src/App.native.tsx b/src/App.native.tsx
index 865e6dc195..ccc7de32d8 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -24,6 +24,7 @@ import {TestCtrls} from 'view/com/testing/TestCtrls'
import {Provider as ShellStateProvider} from 'state/shell'
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
import {Provider as InvitesStateProvider} from 'state/invites'
+import {Provider as PrefsStateProvider} from 'state/preferences'
SplashScreen.preventAutoHideAsync()
@@ -80,11 +81,13 @@ function App() {
return (
-
-
-
-
-
+
+
+
+
+
+
+
)
}
diff --git a/src/App.web.tsx b/src/App.web.tsx
index cfc2a0028b..363161bfc2 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -19,6 +19,7 @@ import {queryClient} from 'lib/react-query'
import {Provider as ShellStateProvider} from 'state/shell'
import {Provider as MutedThreadsProvider} from 'state/muted-threads'
import {Provider as InvitesStateProvider} from 'state/invites'
+import {Provider as PrefsStateProvider} from 'state/preferences'
const InnerApp = observer(function AppImpl() {
const colorMode = useColorMode()
@@ -70,11 +71,13 @@ function App() {
return (
-
-
-
-
-
+
+
+
+
+
+
+
)
}
diff --git a/src/state/models/ui/preferences.ts b/src/state/models/ui/preferences.ts
index d03fa8d24a..9514865920 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 0000000000..56c93f8126
--- /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 {children}
+}
diff --git a/src/state/preferences/languages.tsx b/src/state/preferences/languages.tsx
new file mode 100644
index 0000000000..49b63550df
--- /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(
+ persisted.defaults.languagePrefs,
+)
+const setContext = React.createContext((_: 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 (
+
+
+ {children}
+
+
+ )
+}
+
+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),
+ }))
+}
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx
index a08992df42..4ebbfd666e 100644
--- a/src/view/com/composer/Composer.tsx
+++ b/src/view/com/composer/Composer.tsx
@@ -50,6 +50,12 @@ import {SelectLangBtn} from './select-language/SelectLangBtn'
import {EmojiPickerButton} from './text-input/web/EmojiPicker.web'
import {insertMentionAt} from 'lib/strings/mention-manip'
import {useRequireAltTextEnabled} from '#/state/shell'
+import {
+ useLanguagePrefs,
+ useSetLanguagePrefs,
+ toPostLanguages,
+ savePostLanguageToHistory,
+} from '#/state/preferences/languages'
type Props = ComposerOpts
export const ComposePost = observer(function ComposePost({
@@ -63,6 +69,8 @@ export const ComposePost = observer(function ComposePost({
const {isDesktop, isMobile} = useWebMediaQueries()
const store = useStores()
const requireAltTextEnabled = useRequireAltTextEnabled()
+ const langPrefs = useLanguagePrefs()
+ const setLangPrefs = useSetLanguagePrefs()
const textInput = useRef(null)
const [isKeyboardVisible] = useIsKeyboardVisible({iosUseWillEvents: true})
const [isProcessing, setIsProcessing] = useState(false)
@@ -212,7 +220,7 @@ export const ComposePost = observer(function ComposePost({
labels,
onStateChange: setProcessingState,
knownHandles: autocompleteView.knownHandles,
- langs: store.preferences.postLanguages,
+ langs: toPostLanguages(langPrefs.postLanguage),
})
} catch (e: any) {
if (extLink) {
@@ -234,7 +242,7 @@ export const ComposePost = observer(function ComposePost({
if (!replyTo) {
store.me.mainFeed.onPostCreated()
}
- store.preferences.savePostLanguageToHistory()
+ savePostLanguageToHistory(setLangPrefs)
onPost?.()
onClose()
Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`)
diff --git a/src/view/com/composer/select-language/SelectLangBtn.tsx b/src/view/com/composer/select-language/SelectLangBtn.tsx
index 4faac37504..6465423879 100644
--- a/src/view/com/composer/select-language/SelectLangBtn.tsx
+++ b/src/view/com/composer/select-language/SelectLangBtn.tsx
@@ -15,10 +15,18 @@ import {usePalette} from 'lib/hooks/usePalette'
import {useStores} from 'state/index'
import {isNative} from 'platform/detection'
import {codeToLanguageName} from '../../../../locale/helpers'
+import {
+ useLanguagePrefs,
+ useSetLanguagePrefs,
+ toPostLanguages,
+ hasPostLanguage,
+} from '#/state/preferences/languages'
export const SelectLangBtn = observer(function SelectLangBtn() {
const pal = usePalette('default')
const store = useStores()
+ const langPrefs = useLanguagePrefs()
+ const setLangPrefs = useSetLanguagePrefs()
const onPressMore = useCallback(async () => {
if (isNative) {
@@ -29,8 +37,7 @@ export const SelectLangBtn = observer(function SelectLangBtn() {
store.shell.openModal({name: 'post-languages-settings'})
}, [store])
- const postLanguagesPref = store.preferences.postLanguages
- const postLanguagePref = store.preferences.postLanguage
+ const postLanguagesPref = toPostLanguages(langPrefs.postLanguage)
const items: DropdownItem[] = useMemo(() => {
let arr: DropdownItemButton[] = []
@@ -49,13 +56,14 @@ export const SelectLangBtn = observer(function SelectLangBtn() {
arr.push({
icon:
- langCodes.every(code => store.preferences.hasPostLanguage(code)) &&
- langCodes.length === postLanguagesPref.length
+ langCodes.every(code =>
+ hasPostLanguage(langPrefs.postLanguage, code),
+ ) && langCodes.length === postLanguagesPref.length
? ['fas', 'circle-dot']
: ['far', 'circle'],
label: langName,
onPress() {
- store.preferences.setPostLanguage(commaSeparatedLangCodes)
+ setLangPrefs(v => ({...v, postLanguage: commaSeparatedLangCodes}))
},
})
}
@@ -65,11 +73,11 @@ export const SelectLangBtn = observer(function SelectLangBtn() {
* Re-join here after sanitization bc postLanguageHistory is an array of
* comma-separated strings too
*/
- add(postLanguagePref)
+ add(langPrefs.postLanguage)
}
// comma-separted strings of lang codes that have been used in the past
- for (const lang of store.preferences.postLanguageHistory) {
+ for (const lang of langPrefs.postLanguageHistory) {
add(lang)
}
@@ -82,7 +90,7 @@ export const SelectLangBtn = observer(function SelectLangBtn() {
onPress: onPressMore,
},
]
- }, [store.preferences, onPressMore, postLanguagePref, postLanguagesPref])
+ }, [onPressMore, langPrefs, setLangPrefs, postLanguagesPref])
return (
{
@@ -29,23 +36,23 @@ export function Component({}: {}) {
// sort so that device & selected languages are on top, then alphabetically
langs.sort((a, b) => {
const hasA =
- store.preferences.hasContentLanguage(a.code2) ||
+ langPrefs.contentLanguages.includes(a.code2) ||
deviceLocales.includes(a.code2)
const hasB =
- store.preferences.hasContentLanguage(b.code2) ||
+ langPrefs.contentLanguages.includes(b.code2) ||
deviceLocales.includes(b.code2)
if (hasA === hasB) return a.name.localeCompare(b.name)
if (hasA) return -1
return 1
})
return langs
- }, [store])
+ }, [langPrefs])
const onPress = React.useCallback(
(code2: string) => {
- store.preferences.toggleContentLanguage(code2)
+ toggleContentLanguage(langPrefs, setLangPrefs, code2)
},
- [store],
+ [langPrefs, setLangPrefs],
)
return (
diff --git a/src/view/com/modals/lang-settings/LanguageToggle.tsx b/src/view/com/modals/lang-settings/LanguageToggle.tsx
index 187b46e8cb..86e38a4d29 100644
--- a/src/view/com/modals/lang-settings/LanguageToggle.tsx
+++ b/src/view/com/modals/lang-settings/LanguageToggle.tsx
@@ -3,7 +3,7 @@ import {StyleSheet} from 'react-native'
import {usePalette} from 'lib/hooks/usePalette'
import {observer} from 'mobx-react-lite'
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
-import {useStores} from 'state/index'
+import {useLanguagePrefs, toPostLanguages} from '#/state/preferences/languages'
export const LanguageToggle = observer(function LanguageToggleImpl({
code2,
@@ -17,17 +17,17 @@ export const LanguageToggle = observer(function LanguageToggleImpl({
langType: 'contentLanguages' | 'postLanguages'
}) {
const pal = usePalette('default')
- const store = useStores()
+ const langPrefs = useLanguagePrefs()
- const isSelected = store.preferences[langType].includes(code2)
+ const values =
+ langType === 'contentLanguages'
+ ? langPrefs.contentLanguages
+ : toPostLanguages(langPrefs.postLanguage)
+ const isSelected = values.includes(code2)
// enforce a max of 3 selections for post languages
let isDisabled = false
- if (
- langType === 'postLanguages' &&
- store.preferences[langType].length >= 3 &&
- !isSelected
- ) {
+ if (langType === 'postLanguages' && values.length >= 3 && !isSelected) {
isDisabled = true
}
diff --git a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
index d74d884cc0..435fb9e1ac 100644
--- a/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
+++ b/src/view/com/modals/lang-settings/PostLanguagesSettings.tsx
@@ -10,11 +10,19 @@ import {deviceLocales} from 'platform/detection'
import {LANGUAGES, LANGUAGES_MAP_CODE2} from '../../../../locale/languages'
import {ConfirmLanguagesButton} from './ConfirmLanguagesButton'
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
+import {
+ useLanguagePrefs,
+ useSetLanguagePrefs,
+ hasPostLanguage,
+ togglePostLanguage,
+} from '#/state/preferences/languages'
export const snapPoints = ['100%']
export const Component = observer(function PostLanguagesSettingsImpl() {
const store = useStores()
+ const langPrefs = useLanguagePrefs()
+ const setLangPrefs = useSetLanguagePrefs()
const pal = usePalette('default')
const {isMobile} = useWebMediaQueries()
const onPressDone = React.useCallback(() => {
@@ -30,23 +38,23 @@ export const Component = observer(function PostLanguagesSettingsImpl() {
// sort so that device & selected languages are on top, then alphabetically
langs.sort((a, b) => {
const hasA =
- store.preferences.hasPostLanguage(a.code2) ||
+ hasPostLanguage(langPrefs.postLanguage, a.code2) ||
deviceLocales.includes(a.code2)
const hasB =
- store.preferences.hasPostLanguage(b.code2) ||
+ hasPostLanguage(langPrefs.postLanguage, b.code2) ||
deviceLocales.includes(b.code2)
if (hasA === hasB) return a.name.localeCompare(b.name)
if (hasA) return -1
return 1
})
return langs
- }, [store])
+ }, [langPrefs])
const onPress = React.useCallback(
(code2: string) => {
- store.preferences.togglePostLanguage(code2)
+ togglePostLanguage(langPrefs, setLangPrefs, code2)
},
- [store],
+ [langPrefs, setLangPrefs],
)
return (
@@ -70,14 +78,11 @@ export const Component = observer(function PostLanguagesSettingsImpl() {
{languages.map(lang => {
- const isSelected = store.preferences.hasPostLanguage(lang.code2)
+ const isSelected = hasPostLanguage(langPrefs.postLanguage, lang.code2)
// enforce a max of 3 selections for post languages
let isDisabled = false
- if (
- store.preferences.postLanguage.split(',').length >= 3 &&
- !isSelected
- ) {
+ if (langPrefs.postLanguage.split(',').length >= 3 && !isSelected) {
isDisabled = true
}
diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx
index 9aec638ec2..b72121a375 100644
--- a/src/view/com/post-thread/PostThreadItem.tsx
+++ b/src/view/com/post-thread/PostThreadItem.tsx
@@ -38,6 +38,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {MAX_POST_LINES} from 'lib/constants'
import {logger} from '#/logger'
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
+import {useLanguagePrefs} from '#/state/preferences'
export const PostThreadItem = observer(function PostThreadItem({
item,
@@ -54,6 +55,7 @@ export const PostThreadItem = observer(function PostThreadItem({
const store = useStores()
const mutedThreads = useMutedThreads()
const toggleThreadMute = useToggleThreadMute()
+ const langPrefs = useLanguagePrefs()
const [deleted, setDeleted] = React.useState(false)
const [limitLines, setLimitLines] = React.useState(
countLines(item.richText?.text) >= MAX_POST_LINES,
@@ -85,15 +87,15 @@ export const PostThreadItem = observer(function PostThreadItem({
const translatorUrl = getTranslatorLink(
record?.text || '',
- store.preferences.primaryLanguage,
+ langPrefs.primaryLanguage,
)
const needsTranslation = useMemo(
() =>
Boolean(
- store.preferences.primaryLanguage &&
- !isPostInLanguage(item.post, [store.preferences.primaryLanguage]),
+ langPrefs.primaryLanguage &&
+ !isPostInLanguage(item.post, [langPrefs.primaryLanguage]),
),
- [item.post, store.preferences.primaryLanguage],
+ [item.post, langPrefs.primaryLanguage],
)
const onPressReply = React.useCallback(() => {
diff --git a/src/view/com/post/Post.tsx b/src/view/com/post/Post.tsx
index db490333da..667584f68e 100644
--- a/src/view/com/post/Post.tsx
+++ b/src/view/com/post/Post.tsx
@@ -34,6 +34,7 @@ import {MAX_POST_LINES} from 'lib/constants'
import {countLines} from 'lib/strings/helpers'
import {logger} from '#/logger'
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
+import {useLanguagePrefs} from '#/state/preferences'
export const Post = observer(function PostImpl({
view,
@@ -109,6 +110,7 @@ const PostLoaded = observer(function PostLoadedImpl({
const store = useStores()
const mutedThreads = useMutedThreads()
const toggleThreadMute = useToggleThreadMute()
+ const langPrefs = useLanguagePrefs()
const [limitLines, setLimitLines] = React.useState(
countLines(item.richText?.text) >= MAX_POST_LINES,
)
@@ -125,7 +127,7 @@ const PostLoaded = observer(function PostLoadedImpl({
const translatorUrl = getTranslatorLink(
record?.text || '',
- store.preferences.primaryLanguage,
+ langPrefs.primaryLanguage,
)
const onPressReply = React.useCallback(() => {
diff --git a/src/view/com/posts/FeedItem.tsx b/src/view/com/posts/FeedItem.tsx
index 772bb25614..527cbb76fc 100644
--- a/src/view/com/posts/FeedItem.tsx
+++ b/src/view/com/posts/FeedItem.tsx
@@ -34,6 +34,7 @@ import {MAX_POST_LINES} from 'lib/constants'
import {countLines} from 'lib/strings/helpers'
import {logger} from '#/logger'
import {useMutedThreads, useToggleThreadMute} from '#/state/muted-threads'
+import {useLanguagePrefs} from '#/state/preferences'
export const FeedItem = observer(function FeedItemImpl({
item,
@@ -50,6 +51,7 @@ export const FeedItem = observer(function FeedItemImpl({
showReplyLine?: boolean
}) {
const store = useStores()
+ const langPrefs = useLanguagePrefs()
const pal = usePalette('default')
const mutedThreads = useMutedThreads()
const toggleThreadMute = useToggleThreadMute()
@@ -75,7 +77,7 @@ export const FeedItem = observer(function FeedItemImpl({
}, [record?.reply])
const translatorUrl = getTranslatorLink(
record?.text || '',
- store.preferences.primaryLanguage,
+ langPrefs.primaryLanguage,
)
const onPressReply = React.useCallback(() => {
diff --git a/src/view/screens/AppPasswords.tsx b/src/view/screens/AppPasswords.tsx
index 74d293ef4a..b654055c47 100644
--- a/src/view/screens/AppPasswords.tsx
+++ b/src/view/screens/AppPasswords.tsx
@@ -17,6 +17,7 @@ import {useFocusEffect} from '@react-navigation/native'
import {ViewHeader} from '../com/util/ViewHeader'
import {CenteredView} from 'view/com/util/Views'
import {useSetMinimalShellMode} from '#/state/shell'
+import {useLanguagePrefs} from '#/state/preferences'
type Props = NativeStackScreenProps
export const AppPasswords = withAuthRequired(
@@ -161,6 +162,7 @@ function AppPassword({
}) {
const pal = usePalette('default')
const store = useStores()
+ const {contentLanguages} = useLanguagePrefs()
const onDelete = React.useCallback(async () => {
store.shell.openModal({
@@ -174,8 +176,6 @@ function AppPassword({
})
}, [store, name])
- const {contentLanguages} = store.preferences
-
const primaryLocale =
contentLanguages.length > 0 ? contentLanguages[0] : 'en-US'
diff --git a/src/view/screens/LanguageSettings.tsx b/src/view/screens/LanguageSettings.tsx
index a68a3b5e32..4cf178949e 100644
--- a/src/view/screens/LanguageSettings.tsx
+++ b/src/view/screens/LanguageSettings.tsx
@@ -19,6 +19,7 @@ import {useFocusEffect} from '@react-navigation/native'
import {LANGUAGES} from 'lib/../locale/languages'
import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select'
import {useSetMinimalShellMode} from '#/state/shell'
+import {useLanguagePrefs, useSetLanguagePrefs} from '#/state/preferences'
type Props = NativeStackScreenProps
@@ -27,6 +28,8 @@ export const LanguageSettingsScreen = observer(function LanguageSettingsImpl(
) {
const pal = usePalette('default')
const store = useStores()
+ const langPrefs = useLanguagePrefs()
+ const setLangPrefs = useSetLanguagePrefs()
const {isTabletOrDesktop} = useWebMediaQueries()
const {screen, track} = useAnalytics()
const setMinimalShellMode = useSetMinimalShellMode()
@@ -45,21 +48,23 @@ export const LanguageSettingsScreen = observer(function LanguageSettingsImpl(
const onChangePrimaryLanguage = React.useCallback(
(value: Parameters[0]) => {
- store.preferences.setPrimaryLanguage(value)
+ if (langPrefs.primaryLanguage !== value) {
+ setLangPrefs(v => ({...v, primaryLanguage: value}))
+ }
},
- [store.preferences],
+ [langPrefs, setLangPrefs],
)
const myLanguages = React.useMemo(() => {
return (
- store.preferences.contentLanguages
+ langPrefs.contentLanguages
.map(lang => LANGUAGES.find(l => l.code2 === lang))
.filter(Boolean)
// @ts-ignore
.map(l => l.name)
.join(', ')
)
- }, [store.preferences.contentLanguages])
+ }, [langPrefs.contentLanguages])
return (
Boolean(l.code2)).map(l => ({
label: l.name,