forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 💫
1import {useCallback, useMemo, useState} from 'react'
2import {View} from 'react-native'
3import {msg} from '@lingui/core/macro'
4import {useLingui} from '@lingui/react'
5import {Trans} from '@lingui/react/macro'
6
7import {
8 type CommonNavigatorParams,
9 type NativeStackScreenProps,
10} from '#/lib/routes/types'
11import {languageName, sanitizeAppLanguageSetting} from '#/locale/helpers'
12import {APP_LANGUAGES, LANGUAGES} from '#/locale/languages'
13import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences'
14import {atoms as a, web} from '#/alf'
15import {Admonition} from '#/components/Admonition'
16import {Button} from '#/components/Button'
17import {useDialogControl} from '#/components/Dialog'
18import {LanguageSelectDialog} from '#/components/dialogs/LanguageSelectDialog'
19import * as Toggle from '#/components/forms/Toggle'
20import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
21import * as Layout from '#/components/Layout'
22import * as Select from '#/components/Select'
23import {Text} from '#/components/Typography'
24import * as SettingsList from './components/SettingsList'
25
26const DEDUPED_LANGUAGES = LANGUAGES.filter(
27 (lang, i, arr) =>
28 lang.code2 && arr.findIndex(l => l.code2 === lang.code2) === i,
29)
30
31type Props = NativeStackScreenProps<CommonNavigatorParams, 'LanguageSettings'>
32export function LanguageSettingsScreen({}: Props) {
33 const {_} = useLingui()
34 const langPrefs = useLanguagePrefs()
35 const setLangPrefs = useLanguagePrefsApi()
36
37 const contentLanguagePrefsControl = useDialogControl()
38
39 const onChangePrimaryLanguage = useCallback(
40 (value: string) => {
41 if (!value) return
42 if (langPrefs.primaryLanguage !== value) {
43 setLangPrefs.setPrimaryLanguage(value)
44 }
45 },
46 [langPrefs, setLangPrefs],
47 )
48
49 const onChangeAppLanguage = useCallback(
50 (value: string) => {
51 if (!value) return
52 if (langPrefs.appLanguage !== value) {
53 setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value))
54 }
55 },
56 [langPrefs, setLangPrefs],
57 )
58
59 const [recentLanguages, setRecentLanguages] = useState<string[]>(
60 langPrefs.contentLanguages,
61 )
62
63 const possibleLanguages = useMemo(() => {
64 return [
65 ...new Set([
66 ...recentLanguages,
67 ...langPrefs.contentLanguages,
68 ...langPrefs.primaryLanguage,
69 ]),
70 ]
71 .map(lang => LANGUAGES.find(l => l.code2 === lang))
72 .filter(x => !!x)
73 }, [recentLanguages, langPrefs.contentLanguages, langPrefs.primaryLanguage])
74
75 return (
76 <Layout.Screen testID="PreferencesLanguagesScreen">
77 <Layout.Header.Outer>
78 <Layout.Header.BackButton />
79 <Layout.Header.Content>
80 <Layout.Header.TitleText>
81 <Trans>Languages</Trans>
82 </Layout.Header.TitleText>
83 </Layout.Header.Content>
84 <Layout.Header.Slot />
85 </Layout.Header.Outer>
86 <Layout.Content>
87 <SettingsList.Container>
88 <SettingsList.Group iconInset={false}>
89 <SettingsList.ItemText>
90 <Trans>App language</Trans>
91 </SettingsList.ItemText>
92 <View style={[a.gap_md, a.w_full]}>
93 <Text style={[a.leading_snug]}>
94 <Trans>
95 Select which language to use for the app's user interface.
96 </Trans>
97 </Text>
98 <Select.Root
99 value={sanitizeAppLanguageSetting(langPrefs.appLanguage)}
100 onValueChange={onChangeAppLanguage}>
101 <Select.Trigger label={_(msg`Select app language`)}>
102 <Select.ValueText />
103 <Select.Icon />
104 </Select.Trigger>
105 <Select.Content
106 label={_(msg`App language`)}
107 renderItem={({label, value}) => (
108 <Select.Item value={value} label={label}>
109 <Select.ItemIndicator />
110 <Select.ItemText>{label}</Select.ItemText>
111 </Select.Item>
112 )}
113 items={APP_LANGUAGES.map(l => ({
114 label: l.name,
115 value: l.code2,
116 }))}
117 />
118 </Select.Root>
119 </View>
120 </SettingsList.Group>
121 <SettingsList.Divider />
122 <SettingsList.Group iconInset={false}>
123 <SettingsList.ItemText>
124 <Trans>Primary language</Trans>
125 </SettingsList.ItemText>
126 <View style={[a.gap_md, a.w_full]}>
127 <Text style={[a.leading_snug]}>
128 <Trans>
129 Select your preferred language for translations in your feed.
130 </Trans>
131 </Text>
132 <Select.Root
133 value={langPrefs.primaryLanguage}
134 onValueChange={onChangePrimaryLanguage}>
135 <Select.Trigger label={_(msg`Select primary language`)}>
136 <Select.ValueText />
137 <Select.Icon />
138 </Select.Trigger>
139 <Select.Content
140 label={_(msg`Primary language`)}
141 renderItem={({label, value}) => (
142 <Select.Item value={value} label={label}>
143 <Select.ItemIndicator />
144 <Select.ItemText>{label}</Select.ItemText>
145 </Select.Item>
146 )}
147 items={DEDUPED_LANGUAGES.map(l => ({
148 label: languageName(l, langPrefs.appLanguage),
149 value: l.code2,
150 })).sort((a, b) =>
151 a.label.localeCompare(b.label, langPrefs.appLanguage),
152 )}
153 />
154 </Select.Root>
155 </View>
156 </SettingsList.Group>
157 <SettingsList.Divider />
158 <SettingsList.Group iconInset={false}>
159 <SettingsList.ItemText>
160 <Trans>Content languages</Trans>
161 </SettingsList.ItemText>
162 <View style={[a.gap_md]}>
163 <Text style={[a.leading_snug]}>
164 <Trans>
165 Select which languages you want your subscribed feeds to
166 include. If none are selected, all languages will be shown.
167 </Trans>
168 </Text>
169
170 {langPrefs.contentLanguages.length === 0 && (
171 <Admonition type="info">
172 <Trans>All languages will be shown in your feeds.</Trans>
173 </Admonition>
174 )}
175
176 <View style={[a.w_full, web({maxWidth: 400})]}>
177 <Toggle.Group
178 label={_(msg`Select content languages`)}
179 values={langPrefs.contentLanguages}
180 onChange={setLangPrefs.setContentLanguages}>
181 <Toggle.PanelGroup>
182 {possibleLanguages.map((language, index) => {
183 const name = languageName(language, langPrefs.appLanguage)
184 return (
185 <Toggle.Item
186 key={language.code2}
187 name={language.code2}
188 label={name}>
189 {({selected}) => (
190 <Toggle.Panel
191 active={selected}
192 adjacent={index === 0 ? 'trailing' : 'both'}>
193 <Toggle.Checkbox />
194 <Toggle.PanelText>{name}</Toggle.PanelText>
195 </Toggle.Panel>
196 )}
197 </Toggle.Item>
198 )
199 })}
200 <Button
201 label={_(msg`Add more languages…`)}
202 onPress={contentLanguagePrefsControl.open}>
203 <Toggle.Panel adjacent="leading">
204 <Toggle.PanelIcon icon={PlusIcon} />
205 <Toggle.PanelText>
206 <Trans>Add more languages…</Trans>
207 </Toggle.PanelText>
208 </Toggle.Panel>
209 </Button>
210 </Toggle.PanelGroup>
211 </Toggle.Group>
212 </View>
213
214 <LanguageSelectDialog
215 control={contentLanguagePrefsControl}
216 titleText={<Trans>Select content languages</Trans>}
217 subtitleText={
218 <Trans>
219 If none are selected, all languages will be shown in your
220 feeds.
221 </Trans>
222 }
223 currentLanguages={langPrefs.contentLanguages}
224 onSelectLanguages={languages => {
225 setLangPrefs.setContentLanguages(languages)
226 setRecentLanguages(recent => [
227 ...new Set([...recent, ...languages]),
228 ])
229 }}
230 />
231 </View>
232 </SettingsList.Group>
233 </SettingsList.Container>
234 </Layout.Content>
235 </Layout.Screen>
236 )
237}