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

Configure Feed

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

at f8975036440051185486f6b2c00a201ef2e18a8c 378 lines 12 kB view raw
1import {useCallback} from 'react' 2import {View} from 'react-native' 3import Animated, { 4 FadeInUp, 5 FadeOutUp, 6 LayoutAnimationConfig, 7 LinearTransition, 8} from 'react-native-reanimated' 9import {msg, Trans} from '@lingui/macro' 10import {useLingui} from '@lingui/react' 11 12import { 13 type CommonNavigatorParams, 14 type NativeStackScreenProps, 15} from '#/lib/routes/types' 16import { 17 useEnableSquareAvatars, 18 useSetEnableSquareAvatars, 19} from '#/state/preferences/enable-square-avatars' 20import { 21 useEnableSquareButtons, 22 useSetEnableSquareButtons, 23} from '#/state/preferences/enable-square-buttons' 24import {useKawaiiMode, useSetKawaiiMode} from '#/state/preferences/kawaii' 25import {useSetThemePrefs, useThemePrefs} from '#/state/shell' 26import {SettingsListItem as AppIconSettingsListItem} from '#/screens/Settings/AppIconSettings/SettingsListItem' 27import {type Alf, atoms as a, native, useAlf, useTheme} from '#/alf' 28import * as SegmentedControl from '#/components/forms/SegmentedControl' 29import {Slider} from '#/components/forms/Slider' 30import * as Toggle from '#/components/forms/Toggle' 31import {Circle_And_Square_Stroke1_Corner0_Rounded_Filled as SquareIcon} from '#/components/icons/CircleAndSquare' 32import {ColorPalette_Stroke2_Corner0_Rounded as ColorPaletteIcon} from '#/components/icons/ColorPalette' 33import {type Props as SVGIconProps} from '#/components/icons/common' 34import {Moon_Stroke2_Corner0_Rounded as MoonIcon} from '#/components/icons/Moon' 35import {Phone_Stroke2_Corner0_Rounded as PhoneIcon} from '#/components/icons/Phone' 36import {Sparkle_Stroke2_Corner0_Rounded as SparkleIcon} from '#/components/icons/Sparkle' 37import {TextSize_Stroke2_Corner0_Rounded as TextSize} from '#/components/icons/TextSize' 38import {TitleCase_Stroke2_Corner0_Rounded as Aa} from '#/components/icons/TitleCase' 39import * as Layout from '#/components/Layout' 40import {Text} from '#/components/Typography' 41import {IS_NATIVE} from '#/env' 42import {IS_INTERNAL} from '#/env' 43import * as SettingsList from './components/SettingsList' 44 45type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'> 46export function AppearanceSettingsScreen({}: Props) { 47 const {_} = useLingui() 48 const {fonts} = useAlf() 49 const t = useTheme() 50 51 const {colorMode, colorScheme, darkTheme, hue} = useThemePrefs() 52 const {setColorMode, setColorScheme, setDarkTheme, setHue} = 53 useSetThemePrefs() 54 55 const kawaiiMode = useKawaiiMode() 56 const setKawaiiMode = useSetKawaiiMode() 57 58 const enableSquareAvatars = useEnableSquareAvatars() 59 const setEnableSquareAvatars = useSetEnableSquareAvatars() 60 61 const enableSquareButtons = useEnableSquareButtons() 62 const setEnableSquareButtons = useSetEnableSquareButtons() 63 64 const onChangeAppearance = useCallback( 65 (value: 'light' | 'system' | 'dark') => { 66 setColorMode(value) 67 }, 68 [setColorMode], 69 ) 70 71 const onChangeScheme = useCallback( 72 ( 73 value: 74 | 'witchsky' 75 | 'bluesky' 76 | 'blacksky' 77 | 'deer' 78 | 'zeppelin' 79 | 'kitty' 80 | 'reddwarf', 81 ) => { 82 setColorScheme(value) 83 }, 84 [setColorScheme], 85 ) 86 87 const onChangeDarkTheme = useCallback( 88 (value: 'dim' | 'dark') => { 89 setDarkTheme(value) 90 }, 91 [setDarkTheme], 92 ) 93 94 const onChangeFontFamily = useCallback( 95 (value: 'system' | 'theme') => { 96 fonts.setFontFamily(value) 97 }, 98 [fonts], 99 ) 100 101 const onChangeFontScale = useCallback( 102 (value: Alf['fonts']['scale']) => { 103 fonts.setFontScale(value) 104 }, 105 [fonts], 106 ) 107 108 const colorSchemes = [ 109 {name: 'witchsky', label: _(msg`Witchsky`)}, 110 {name: 'bluesky', label: _(msg`Bluesky`)}, 111 {name: 'blacksky', label: _(msg`Blacksky`)}, 112 {name: 'deer', label: _(msg`Deer`)}, 113 {name: 'zeppelin', label: _(msg`Zeppelin`)}, 114 {name: 'kitty', label: _(msg`Kitty`)}, 115 {name: 'reddwarf', label: _(msg`Red Dwarf`)}, 116 ] 117 118 return ( 119 <LayoutAnimationConfig skipExiting skipEntering> 120 <Layout.Screen testID="preferencesThreadsScreen"> 121 <Layout.Header.Outer> 122 <Layout.Header.BackButton /> 123 <Layout.Header.Content> 124 <Layout.Header.TitleText> 125 <Trans>Appearance</Trans> 126 </Layout.Header.TitleText> 127 </Layout.Header.Content> 128 <Layout.Header.Slot /> 129 </Layout.Header.Outer> 130 <Layout.Content> 131 <SettingsList.Container> 132 <AppearanceToggleButtonGroup 133 title={_(msg`Color mode`)} 134 icon={PhoneIcon} 135 items={[ 136 { 137 label: _(msg`System`), 138 name: 'system', 139 }, 140 { 141 label: _(msg`Light`), 142 name: 'light', 143 }, 144 { 145 label: _(msg`Dark`), 146 name: 'dark', 147 }, 148 ]} 149 value={colorMode} 150 onChange={onChangeAppearance} 151 /> 152 153 {colorMode !== 'light' && ( 154 <Animated.View 155 entering={native(FadeInUp)} 156 exiting={native(FadeOutUp)}> 157 <AppearanceToggleButtonGroup 158 title={_(msg`Dark theme`)} 159 icon={MoonIcon} 160 items={[ 161 { 162 label: _(msg`Dim`), 163 name: 'dim', 164 }, 165 { 166 label: _(msg`Dark`), 167 name: 'dark', 168 }, 169 ]} 170 value={darkTheme ?? 'dim'} 171 onChange={onChangeDarkTheme} 172 /> 173 </Animated.View> 174 )} 175 176 <SettingsList.Group> 177 <SettingsList.ItemIcon icon={ColorPaletteIcon} /> 178 <SettingsList.ItemText> 179 <Trans>Color Theme</Trans> 180 </SettingsList.ItemText> 181 <View style={[a.w_full, a.gap_md]}> 182 <Text style={[a.flex_1, t.atoms.text_contrast_medium]}> 183 <Trans>Choose which color scheme to use:</Trans> 184 </Text> 185 <Toggle.Group 186 label={_(msg`Color Theme`)} 187 type="radio" 188 values={[colorScheme]} 189 onChange={([value]) => 190 onChangeScheme(value as typeof colorScheme) 191 }> 192 <View style={[a.gap_sm, a.flex_1]}> 193 {colorSchemes.map(({name, label}) => ( 194 <Toggle.Item key={name} name={name} label={label}> 195 <Toggle.Radio /> 196 <Toggle.LabelText> 197 <Trans>{label}</Trans> 198 </Toggle.LabelText> 199 </Toggle.Item> 200 ))} 201 </View> 202 </Toggle.Group> 203 <Text style={[a.flex_1, t.atoms.text_contrast_medium]}> 204 <Trans>Hue shift the colors:</Trans> 205 </Text> 206 <Slider 207 value={hue} 208 onValueChange={setHue} 209 minimumValue={0} 210 maximumValue={360} 211 step={1} 212 debounceFull={true} 213 /> 214 </View> 215 </SettingsList.Group> 216 217 <Animated.View layout={native(LinearTransition)}> 218 <SettingsList.Divider /> 219 220 <AppearanceToggleButtonGroup 221 title={_(msg`Font`)} 222 description={_( 223 msg`For the best experience, we recommend using the theme font.`, 224 )} 225 icon={Aa} 226 items={[ 227 { 228 label: _(msg`System`), 229 name: 'system', 230 }, 231 { 232 label: _(msg`Theme`), 233 name: 'theme', 234 }, 235 ]} 236 value={fonts.family} 237 onChange={onChangeFontFamily} 238 /> 239 240 <AppearanceToggleButtonGroup 241 title={_(msg`Font size`)} 242 icon={TextSize} 243 items={[ 244 { 245 label: _(msg`Smaller`), 246 name: '-1', 247 }, 248 { 249 label: _(msg`Default`), 250 name: '0', 251 }, 252 { 253 label: _(msg`Larger`), 254 name: '1', 255 }, 256 ]} 257 value={fonts.scale} 258 onChange={onChangeFontScale} 259 /> 260 261 <SettingsList.Divider /> 262 263 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 264 <SettingsList.ItemIcon icon={SparkleIcon} /> 265 <SettingsList.ItemText> 266 <Trans>Logo</Trans> 267 </SettingsList.ItemText> 268 <Toggle.Item 269 name="kawaii_mode" 270 label={_(msg`Enable kawaii logo`)} 271 value={kawaiiMode} 272 onChange={value => setKawaiiMode(value)} 273 style={[a.w_full]}> 274 <Toggle.LabelText style={[a.flex_1]}> 275 <Trans>Enable kawaii logo</Trans> 276 </Toggle.LabelText> 277 <Toggle.Platform /> 278 </Toggle.Item> 279 </SettingsList.Group> 280 281 <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 282 <SettingsList.ItemIcon icon={SquareIcon} /> 283 <SettingsList.ItemText> 284 <Trans>Shapes</Trans> 285 </SettingsList.ItemText> 286 <Toggle.Item 287 name="enable_square_avatars" 288 label={_(msg`Enable square avatars`)} 289 value={enableSquareAvatars} 290 onChange={value => setEnableSquareAvatars(value)} 291 style={[a.w_full]}> 292 <Toggle.LabelText style={[a.flex_1]}> 293 <Trans>Enable square avatars</Trans> 294 </Toggle.LabelText> 295 <Toggle.Platform /> 296 </Toggle.Item> 297 298 <Toggle.Item 299 name="enable_square_buttons" 300 label={_(msg`Enable square buttons`)} 301 value={enableSquareButtons} 302 onChange={value => setEnableSquareButtons(value)} 303 style={[a.w_full]}> 304 <Toggle.LabelText style={[a.flex_1]}> 305 <Trans>Enable square buttons</Trans> 306 </Toggle.LabelText> 307 <Toggle.Platform /> 308 </Toggle.Item> 309 </SettingsList.Group> 310 {IS_NATIVE && IS_INTERNAL && ( 311 <> 312 <SettingsList.Divider /> 313 <AppIconSettingsListItem /> 314 </> 315 )} 316 </Animated.View> 317 </SettingsList.Container> 318 </Layout.Content> 319 </Layout.Screen> 320 </LayoutAnimationConfig> 321 ) 322} 323 324export function AppearanceToggleButtonGroup<T extends string>({ 325 title, 326 description, 327 icon: Icon, 328 items, 329 value, 330 onChange, 331}: { 332 title: string 333 description?: string 334 icon: React.ComponentType<SVGIconProps> 335 items: { 336 label: string 337 name: T 338 }[] 339 value: T 340 onChange: (value: T) => void 341}) { 342 const t = useTheme() 343 return ( 344 <> 345 <SettingsList.Group contentContainerStyle={[a.gap_sm]} iconInset={false}> 346 <SettingsList.ItemIcon icon={Icon} /> 347 <SettingsList.ItemText>{title}</SettingsList.ItemText> 348 {description && ( 349 <Text 350 style={[ 351 a.text_sm, 352 a.leading_snug, 353 t.atoms.text_contrast_medium, 354 a.w_full, 355 ]}> 356 {description} 357 </Text> 358 )} 359 <SegmentedControl.Root 360 type="radio" 361 label={title} 362 value={value} 363 onChange={onChange}> 364 {items.map(item => ( 365 <SegmentedControl.Item 366 key={item.name} 367 label={item.label} 368 value={item.name}> 369 <SegmentedControl.ItemText> 370 {item.label} 371 </SegmentedControl.ItemText> 372 </SegmentedControl.Item> 373 ))} 374 </SegmentedControl.Root> 375 </SettingsList.Group> 376 </> 377 ) 378}