Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 254 lines 9.7 kB view raw
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 // changing langPrefs causes a slow re-render, so we use a local state copy 38 // and update that first to drive the UI on this screen to keep it snappy 39 const [contentLanguages, _setContentLanguages] = useState( 40 langPrefs.contentLanguages, 41 ) 42 const setContentLanguages = useCallback( 43 (languages: string[]) => { 44 _setContentLanguages(languages) 45 // TODO: try using startTransition/useOptimistic when we switch to New Arch 46 // Old arch doesn't support concurrent react features so use rAF instead 47 requestAnimationFrame(() => { 48 setLangPrefs.setContentLanguages(languages) 49 }) 50 }, 51 [setLangPrefs], 52 ) 53 54 const contentLanguagePrefsControl = useDialogControl() 55 56 const onChangePrimaryLanguage = useCallback( 57 (value: string) => { 58 if (!value) return 59 if (langPrefs.primaryLanguage !== value) { 60 setLangPrefs.setPrimaryLanguage(value) 61 } 62 }, 63 [langPrefs, setLangPrefs], 64 ) 65 66 const onChangeAppLanguage = useCallback( 67 (value: string) => { 68 if (!value) return 69 if (langPrefs.appLanguage !== value) { 70 setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value)) 71 } 72 }, 73 [langPrefs, setLangPrefs], 74 ) 75 76 const [recentLanguages, setRecentLanguages] = useState<string[]>( 77 langPrefs.contentLanguages, 78 ) 79 80 const possibleLanguages = useMemo(() => { 81 return [ 82 ...new Set([ 83 ...recentLanguages, 84 ...contentLanguages, 85 ...langPrefs.primaryLanguage, 86 ]), 87 ] 88 .map(lang => LANGUAGES.find(l => l.code2 === lang)) 89 .filter(x => !!x) 90 }, [recentLanguages, contentLanguages, langPrefs.primaryLanguage]) 91 92 return ( 93 <Layout.Screen testID="PreferencesLanguagesScreen"> 94 <Layout.Header.Outer> 95 <Layout.Header.BackButton /> 96 <Layout.Header.Content> 97 <Layout.Header.TitleText> 98 <Trans>Languages</Trans> 99 </Layout.Header.TitleText> 100 </Layout.Header.Content> 101 <Layout.Header.Slot /> 102 </Layout.Header.Outer> 103 <Layout.Content> 104 <SettingsList.Container> 105 <SettingsList.Group iconInset={false}> 106 <SettingsList.ItemText> 107 <Trans>App language</Trans> 108 </SettingsList.ItemText> 109 <View style={[a.gap_md, a.w_full]}> 110 <Text style={[a.leading_snug]}> 111 <Trans> 112 Select which language to use for the app's user interface. 113 </Trans> 114 </Text> 115 <Select.Root 116 value={sanitizeAppLanguageSetting(langPrefs.appLanguage)} 117 onValueChange={onChangeAppLanguage}> 118 <Select.Trigger label={_(msg`Select app language`)}> 119 <Select.ValueText /> 120 <Select.Icon /> 121 </Select.Trigger> 122 <Select.Content 123 label={_(msg`App language`)} 124 renderItem={({label, value}) => ( 125 <Select.Item value={value} label={label}> 126 <Select.ItemIndicator /> 127 <Select.ItemText>{label}</Select.ItemText> 128 </Select.Item> 129 )} 130 items={APP_LANGUAGES.map(l => ({ 131 label: l.name, 132 value: l.code2, 133 }))} 134 /> 135 </Select.Root> 136 </View> 137 </SettingsList.Group> 138 <SettingsList.Divider /> 139 <SettingsList.Group iconInset={false}> 140 <SettingsList.ItemText> 141 <Trans>Primary language</Trans> 142 </SettingsList.ItemText> 143 <View style={[a.gap_md, a.w_full]}> 144 <Text style={[a.leading_snug]}> 145 <Trans> 146 Select your preferred language for translations in your feed. 147 </Trans> 148 </Text> 149 <Select.Root 150 value={langPrefs.primaryLanguage} 151 onValueChange={onChangePrimaryLanguage}> 152 <Select.Trigger label={_(msg`Select primary language`)}> 153 <Select.ValueText /> 154 <Select.Icon /> 155 </Select.Trigger> 156 <Select.Content 157 label={_(msg`Primary language`)} 158 renderItem={({label, value}) => ( 159 <Select.Item value={value} label={label}> 160 <Select.ItemIndicator /> 161 <Select.ItemText>{label}</Select.ItemText> 162 </Select.Item> 163 )} 164 items={DEDUPED_LANGUAGES.map(l => ({ 165 label: languageName(l, langPrefs.appLanguage), 166 value: l.code2, 167 })).sort((a, b) => 168 a.label.localeCompare(b.label, langPrefs.appLanguage), 169 )} 170 /> 171 </Select.Root> 172 </View> 173 </SettingsList.Group> 174 <SettingsList.Divider /> 175 <SettingsList.Group iconInset={false}> 176 <SettingsList.ItemText> 177 <Trans>Content languages</Trans> 178 </SettingsList.ItemText> 179 <View style={[a.gap_md]}> 180 <Text style={[a.leading_snug]}> 181 <Trans> 182 Select which languages you want your subscribed feeds to 183 include. If none are selected, all languages will be shown. 184 </Trans> 185 </Text> 186 187 {contentLanguages.length === 0 && ( 188 <Admonition type="info"> 189 <Trans>All languages will be shown in your feeds.</Trans> 190 </Admonition> 191 )} 192 193 <View style={[a.w_full, web({maxWidth: 400})]}> 194 <Toggle.Group 195 label={_(msg`Select content languages`)} 196 values={contentLanguages} 197 onChange={setContentLanguages}> 198 <Toggle.PanelGroup> 199 {possibleLanguages.map((language, index) => { 200 const name = languageName(language, langPrefs.appLanguage) 201 return ( 202 <Toggle.Item 203 key={language.code2} 204 name={language.code2} 205 label={name}> 206 {({selected}) => ( 207 <Toggle.Panel 208 active={selected} 209 adjacent={index === 0 ? 'trailing' : 'both'}> 210 <Toggle.Checkbox /> 211 <Toggle.PanelText>{name}</Toggle.PanelText> 212 </Toggle.Panel> 213 )} 214 </Toggle.Item> 215 ) 216 })} 217 <Button 218 label={_(msg`Add more languages…`)} 219 onPress={contentLanguagePrefsControl.open}> 220 <Toggle.Panel adjacent="leading"> 221 <Toggle.PanelIcon icon={PlusIcon} /> 222 <Toggle.PanelText> 223 <Trans>Add more languages</Trans> 224 </Toggle.PanelText> 225 </Toggle.Panel> 226 </Button> 227 </Toggle.PanelGroup> 228 </Toggle.Group> 229 </View> 230 231 <LanguageSelectDialog 232 control={contentLanguagePrefsControl} 233 titleText={<Trans>Select content languages</Trans>} 234 subtitleText={ 235 <Trans> 236 If none are selected, all languages will be shown in your 237 feeds. 238 </Trans> 239 } 240 currentLanguages={contentLanguages} 241 onSelectLanguages={languages => { 242 setContentLanguages(languages) 243 setRecentLanguages(recent => [ 244 ...new Set([...recent, ...languages]), 245 ]) 246 }} 247 /> 248 </View> 249 </SettingsList.Group> 250 </SettingsList.Container> 251 </Layout.Content> 252 </Layout.Screen> 253 ) 254}