Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

at 967b3b49d9b0bdbe9c8fd7ea802ecf780b9e1a0c 236 lines 6.9 kB view raw
1import {useCallback} from 'react' 2import Animated, { 3 FadeInUp, 4 FadeOutUp, 5 LayoutAnimationConfig, 6 LinearTransition, 7} from 'react-native-reanimated' 8import {msg, Trans} from '@lingui/macro' 9import {useLingui} from '@lingui/react' 10 11import { 12 type CommonNavigatorParams, 13 type NativeStackScreenProps, 14} from '#/lib/routes/types' 15import {useSetThemePrefs, useThemePrefs} from '#/state/shell' 16import {SettingsListItem as AppIconSettingsListItem} from '#/screens/Settings/AppIconSettings/SettingsListItem' 17import {type Alf, atoms as a, native, useAlf, useTheme} from '#/alf' 18import * as SegmentedControl from '#/components/forms/SegmentedControl' 19import {type Props as SVGIconProps} from '#/components/icons/common' 20import {Moon_Stroke2_Corner0_Rounded as MoonIcon} from '#/components/icons/Moon' 21import {Phone_Stroke2_Corner0_Rounded as PhoneIcon} from '#/components/icons/Phone' 22import {TextSize_Stroke2_Corner0_Rounded as TextSize} from '#/components/icons/TextSize' 23import {TitleCase_Stroke2_Corner0_Rounded as Aa} from '#/components/icons/TitleCase' 24import * as Layout from '#/components/Layout' 25import {Text} from '#/components/Typography' 26import {IS_NATIVE} from '#/env' 27import {IS_INTERNAL} from '#/env' 28import * as SettingsList from './components/SettingsList' 29 30type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'> 31export function AppearanceSettingsScreen({}: Props) { 32 const {_} = useLingui() 33 const {fonts} = useAlf() 34 35 const {colorMode, darkTheme} = useThemePrefs() 36 const {setColorMode, setDarkTheme} = useSetThemePrefs() 37 38 const onChangeAppearance = useCallback( 39 (value: 'light' | 'system' | 'dark') => { 40 setColorMode(value) 41 }, 42 [setColorMode], 43 ) 44 45 const onChangeDarkTheme = useCallback( 46 (value: 'dim' | 'dark') => { 47 setDarkTheme(value) 48 }, 49 [setDarkTheme], 50 ) 51 52 const onChangeFontFamily = useCallback( 53 (value: 'system' | 'theme') => { 54 fonts.setFontFamily(value) 55 }, 56 [fonts], 57 ) 58 59 const onChangeFontScale = useCallback( 60 (value: Alf['fonts']['scale']) => { 61 fonts.setFontScale(value) 62 }, 63 [fonts], 64 ) 65 66 return ( 67 <LayoutAnimationConfig skipExiting skipEntering> 68 <Layout.Screen testID="preferencesThreadsScreen"> 69 <Layout.Header.Outer> 70 <Layout.Header.BackButton /> 71 <Layout.Header.Content> 72 <Layout.Header.TitleText> 73 <Trans>Appearance</Trans> 74 </Layout.Header.TitleText> 75 </Layout.Header.Content> 76 <Layout.Header.Slot /> 77 </Layout.Header.Outer> 78 <Layout.Content> 79 <SettingsList.Container> 80 <AppearanceToggleButtonGroup 81 title={_(msg`Color mode`)} 82 icon={PhoneIcon} 83 items={[ 84 { 85 label: _(msg`System`), 86 name: 'system', 87 }, 88 { 89 label: _(msg`Light`), 90 name: 'light', 91 }, 92 { 93 label: _(msg`Dark`), 94 name: 'dark', 95 }, 96 ]} 97 value={colorMode} 98 onChange={onChangeAppearance} 99 /> 100 101 {colorMode !== 'light' && ( 102 <Animated.View 103 entering={native(FadeInUp)} 104 exiting={native(FadeOutUp)}> 105 <AppearanceToggleButtonGroup 106 title={_(msg`Dark theme`)} 107 icon={MoonIcon} 108 items={[ 109 { 110 label: _(msg`Dim`), 111 name: 'dim', 112 }, 113 { 114 label: _(msg`Dark`), 115 name: 'dark', 116 }, 117 ]} 118 value={darkTheme ?? 'dim'} 119 onChange={onChangeDarkTheme} 120 /> 121 </Animated.View> 122 )} 123 124 <Animated.View layout={native(LinearTransition)}> 125 <SettingsList.Divider /> 126 127 <AppearanceToggleButtonGroup 128 title={_(msg`Font`)} 129 description={_( 130 msg`For the best experience, we recommend using the theme font.`, 131 )} 132 icon={Aa} 133 items={[ 134 { 135 label: _(msg`System`), 136 name: 'system', 137 }, 138 { 139 label: _(msg`Theme`), 140 name: 'theme', 141 }, 142 ]} 143 value={fonts.family} 144 onChange={onChangeFontFamily} 145 /> 146 147 <AppearanceToggleButtonGroup 148 title={_(msg`Font size`)} 149 icon={TextSize} 150 items={[ 151 { 152 label: _(msg`Smaller`), 153 name: '-1', 154 }, 155 { 156 label: _(msg`Default`), 157 name: '0', 158 }, 159 { 160 label: _(msg`Larger`), 161 name: '1', 162 }, 163 ]} 164 value={fonts.scale} 165 onChange={onChangeFontScale} 166 /> 167 168 {IS_NATIVE && IS_INTERNAL && ( 169 <> 170 <SettingsList.Divider /> 171 <AppIconSettingsListItem /> 172 </> 173 )} 174 </Animated.View> 175 </SettingsList.Container> 176 </Layout.Content> 177 </Layout.Screen> 178 </LayoutAnimationConfig> 179 ) 180} 181 182export function AppearanceToggleButtonGroup<T extends string>({ 183 title, 184 description, 185 icon: Icon, 186 items, 187 value, 188 onChange, 189}: { 190 title: string 191 description?: string 192 icon: React.ComponentType<SVGIconProps> 193 items: { 194 label: string 195 name: T 196 }[] 197 value: T 198 onChange: (value: T) => void 199}) { 200 const t = useTheme() 201 return ( 202 <> 203 <SettingsList.Group contentContainerStyle={[a.gap_sm]} iconInset={false}> 204 <SettingsList.ItemIcon icon={Icon} /> 205 <SettingsList.ItemText>{title}</SettingsList.ItemText> 206 {description && ( 207 <Text 208 style={[ 209 a.text_sm, 210 a.leading_snug, 211 t.atoms.text_contrast_medium, 212 a.w_full, 213 ]}> 214 {description} 215 </Text> 216 )} 217 <SegmentedControl.Root 218 type="radio" 219 label={title} 220 value={value} 221 onChange={onChange}> 222 {items.map(item => ( 223 <SegmentedControl.Item 224 key={item.name} 225 label={item.label} 226 value={item.name}> 227 <SegmentedControl.ItemText> 228 {item.label} 229 </SegmentedControl.ItemText> 230 </SegmentedControl.Item> 231 ))} 232 </SegmentedControl.Root> 233 </SettingsList.Group> 234 </> 235 ) 236}