about summary refs log tree commit diff
path: root/src/screens/Settings/LanguageSettings.tsx
diff options
context:
space:
mode:
authorSamuel Newman <mozzius@protonmail.com>2025-05-06 20:27:05 +0300
committerGitHub <noreply@github.com>2025-05-06 10:27:05 -0700
commit973538d246a3f76550611e438152f1a6cad75f49 (patch)
tree83c7547eb9ba1123bac8ab8ef30f37d5164b3ce2 /src/screens/Settings/LanguageSettings.tsx
parent25f8506c4152840e83ba9210452b60ea5cc0987f (diff)
downloadvoidsky-973538d246a3f76550611e438152f1a6cad75f49.tar.zst
New `Select` component (#8323)
* radix select component on web

* native implementation (wip)

* fix sheet height/padding

* tone down web styles

* react 19 cleanup

* replace primary language select

* change style on native

* get auto placeholder working

* more style tweaks

* replace app language dropdown

* replace rnpickerselect with native select

* rm react-native-picker-select dependency

* rm placeholder, since a value is always selected

* docblock for renderItem

* add more docblocks

* add style prop to item

* pass selectedValue through renderItem

* fix context

* Style overflow buttons

---------

Co-authored-by: Eric Bailey <git@esb.lol>
Diffstat (limited to 'src/screens/Settings/LanguageSettings.tsx')
-rw-r--r--src/screens/Settings/LanguageSettings.tsx185
1 files changed, 44 insertions, 141 deletions
diff --git a/src/screens/Settings/LanguageSettings.tsx b/src/screens/Settings/LanguageSettings.tsx
index 7266dda4a..d035e6592 100644
--- a/src/screens/Settings/LanguageSettings.tsx
+++ b/src/screens/Settings/LanguageSettings.tsx
@@ -1,23 +1,30 @@
 import {useCallback, useMemo} from 'react'
 import {View} from 'react-native'
-import RNPickerSelect, {PickerSelectProps} from 'react-native-picker-select'
 import {msg, Trans} from '@lingui/macro'
 import {useLingui} from '@lingui/react'
 
 import {APP_LANGUAGES, LANGUAGES} from '#/lib/../locale/languages'
-import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
+import {
+  type CommonNavigatorParams,
+  type NativeStackScreenProps,
+} from '#/lib/routes/types'
 import {languageName, sanitizeAppLanguageSetting} from '#/locale/helpers'
 import {useModalControls} from '#/state/modals'
 import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences'
 import {atoms as a, useTheme, web} from '#/alf'
 import {Button, ButtonIcon, ButtonText} from '#/components/Button'
 import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
-import {ChevronBottom_Stroke2_Corner0_Rounded as ChevronDownIcon} from '#/components/icons/Chevron'
 import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/Plus'
 import * as Layout from '#/components/Layout'
+import * as Select from '#/components/Select'
 import {Text} from '#/components/Typography'
 import * as SettingsList from './components/SettingsList'
 
+const DEDUPED_LANGUAGES = LANGUAGES.filter(
+  (lang, i, arr) =>
+    lang.code2 && arr.findIndex(l => l.code2 === lang.code2) === i,
+)
+
 type Props = NativeStackScreenProps<CommonNavigatorParams, 'LanguageSettings'>
 export function LanguageSettingsScreen({}: Props) {
   const {_} = useLingui()
@@ -32,7 +39,7 @@ export function LanguageSettingsScreen({}: Props) {
   }, [openModal])
 
   const onChangePrimaryLanguage = useCallback(
-    (value: Parameters<PickerSelectProps['onValueChange']>[0]) => {
+    (value: string) => {
       if (!value) return
       if (langPrefs.primaryLanguage !== value) {
         setLangPrefs.setPrimaryLanguage(value)
@@ -42,7 +49,7 @@ export function LanguageSettingsScreen({}: Props) {
   )
 
   const onChangeAppLanguage = useCallback(
-    (value: Parameters<PickerSelectProps['onValueChange']>[0]) => {
+    (value: string) => {
       if (!value) return
       if (langPrefs.appLanguage !== value) {
         setLangPrefs.setAppLanguage(sanitizeAppLanguageSetting(value))
@@ -85,79 +92,26 @@ export function LanguageSettingsScreen({}: Props) {
                   Select which language to use for the app's user interface.
                 </Trans>
               </Text>
-              <View style={[a.relative, web([a.w_full, {maxWidth: 400}])]}>
-                <RNPickerSelect
-                  darkTheme={t.scheme === 'dark'}
-                  placeholder={{}}
-                  value={sanitizeAppLanguageSetting(langPrefs.appLanguage)}
-                  onValueChange={onChangeAppLanguage}
-                  items={APP_LANGUAGES.filter(l => Boolean(l.code2)).map(l => ({
+              <Select.Root
+                value={sanitizeAppLanguageSetting(langPrefs.appLanguage)}
+                onValueChange={onChangeAppLanguage}>
+                <Select.Trigger label={_(msg`Select app language`)}>
+                  <Select.ValueText />
+                  <Select.Icon />
+                </Select.Trigger>
+                <Select.Content
+                  renderItem={({label, value}) => (
+                    <Select.Item value={value} label={label}>
+                      <Select.ItemIndicator />
+                      <Select.ItemText>{label}</Select.ItemText>
+                    </Select.Item>
+                  )}
+                  items={APP_LANGUAGES.map(l => ({
                     label: l.name,
                     value: l.code2,
-                    key: l.code2,
                   }))}
-                  style={{
-                    inputAndroid: {
-                      backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
-                      color: t.atoms.text.color,
-                      fontSize: 14,
-                      letterSpacing: 0.5,
-                      fontWeight: a.font_bold.fontWeight,
-                      paddingHorizontal: 14,
-                      paddingVertical: 8,
-                      borderRadius: a.rounded_xs.borderRadius,
-                    },
-                    inputIOS: {
-                      backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
-                      color: t.atoms.text.color,
-                      fontSize: 14,
-                      letterSpacing: 0.5,
-                      fontWeight: a.font_bold.fontWeight,
-                      paddingHorizontal: 14,
-                      paddingVertical: 8,
-                      borderRadius: a.rounded_xs.borderRadius,
-                    },
-                    inputWeb: {
-                      flex: 1,
-                      width: '100%',
-                      cursor: 'pointer',
-                      // @ts-ignore web only
-                      '-moz-appearance': 'none',
-                      '-webkit-appearance': 'none',
-                      appearance: 'none',
-                      outline: 0,
-                      borderWidth: 0,
-                      backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
-                      color: t.atoms.text.color,
-                      fontSize: 14,
-                      fontFamily: 'inherit',
-                      letterSpacing: 0.5,
-                      fontWeight: a.font_bold.fontWeight,
-                      paddingHorizontal: 14,
-                      paddingVertical: 8,
-                      borderRadius: a.rounded_xs.borderRadius,
-                    },
-                  }}
                 />
-
-                <View
-                  style={[
-                    a.absolute,
-                    t.atoms.bg_contrast_25,
-                    a.rounded_xs,
-                    a.pointer_events_none,
-                    a.align_center,
-                    a.justify_center,
-                    {
-                      top: 1,
-                      right: 1,
-                      bottom: 1,
-                      width: 40,
-                    },
-                  ]}>
-                  <ChevronDownIcon style={[t.atoms.text]} />
-                </View>
-              </View>
+              </Select.Root>
             </View>
           </SettingsList.Group>
           <SettingsList.Divider />
@@ -171,77 +125,26 @@ export function LanguageSettingsScreen({}: Props) {
                   Select your preferred language for translations in your feed.
                 </Trans>
               </Text>
-              <View style={[a.relative, web([a.w_full, {maxWidth: 400}])]}>
-                <RNPickerSelect
-                  darkTheme={t.scheme === 'dark'}
-                  placeholder={{}}
-                  value={langPrefs.primaryLanguage}
-                  onValueChange={onChangePrimaryLanguage}
-                  items={LANGUAGES.filter(l => Boolean(l.code2)).map(l => ({
+              <Select.Root
+                value={langPrefs.primaryLanguage}
+                onValueChange={onChangePrimaryLanguage}>
+                <Select.Trigger label={_(msg`Select primary language`)}>
+                  <Select.ValueText />
+                  <Select.Icon />
+                </Select.Trigger>
+                <Select.Content
+                  renderItem={({label, value}) => (
+                    <Select.Item value={value} label={label}>
+                      <Select.ItemIndicator />
+                      <Select.ItemText>{label}</Select.ItemText>
+                    </Select.Item>
+                  )}
+                  items={DEDUPED_LANGUAGES.map(l => ({
                     label: languageName(l, langPrefs.appLanguage),
                     value: l.code2,
-                    key: l.code2 + l.code3,
                   }))}
-                  style={{
-                    inputAndroid: {
-                      backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
-                      color: t.atoms.text.color,
-                      fontSize: 14,
-                      letterSpacing: 0.5,
-                      fontWeight: a.font_bold.fontWeight,
-                      paddingHorizontal: 14,
-                      paddingVertical: 8,
-                      borderRadius: a.rounded_xs.borderRadius,
-                    },
-                    inputIOS: {
-                      backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
-                      color: t.atoms.text.color,
-                      fontSize: 14,
-                      letterSpacing: 0.5,
-                      fontWeight: a.font_bold.fontWeight,
-                      paddingHorizontal: 14,
-                      paddingVertical: 8,
-                      borderRadius: a.rounded_xs.borderRadius,
-                    },
-                    inputWeb: {
-                      flex: 1,
-                      width: '100%',
-                      cursor: 'pointer',
-                      // @ts-ignore web only
-                      '-moz-appearance': 'none',
-                      '-webkit-appearance': 'none',
-                      appearance: 'none',
-                      outline: 0,
-                      borderWidth: 0,
-                      backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
-                      color: t.atoms.text.color,
-                      fontSize: 14,
-                      fontFamily: 'inherit',
-                      letterSpacing: 0.5,
-                      fontWeight: a.font_bold.fontWeight,
-                      paddingHorizontal: 14,
-                      paddingVertical: 8,
-                      borderRadius: a.rounded_xs.borderRadius,
-                    },
-                  }}
                 />
-
-                <View
-                  style={{
-                    position: 'absolute',
-                    top: 1,
-                    right: 1,
-                    bottom: 1,
-                    width: 40,
-                    backgroundColor: t.atoms.bg_contrast_25.backgroundColor,
-                    borderRadius: a.rounded_xs.borderRadius,
-                    pointerEvents: 'none',
-                    alignItems: 'center',
-                    justifyContent: 'center',
-                  }}>
-                  <ChevronDownIcon style={t.atoms.text} />
-                </View>
-              </View>
+              </Select.Root>
             </View>
           </SettingsList.Group>
           <SettingsList.Divider />