mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {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 {DISCOVER_DEBUG_DIDS} from '#/lib/constants'
12import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
13import {isNative} from '#/platform/detection'
14import {useSession} from '#/state/session'
15import {useSetThemePrefs, useThemePrefs} from '#/state/shell'
16import {Logo} from '#/view/icons/Logo'
17import {atoms as a, native, useAlf, useTheme} from '#/alf'
18import * as ToggleButton from '#/components/forms/ToggleButton'
19import {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 * as SettingsList from './components/SettingsList'
27
28type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppearanceSettings'>
29export function AppearanceSettingsScreen({}: Props) {
30 const {_} = useLingui()
31 const {fonts} = useAlf()
32
33 const {colorMode, darkTheme} = useThemePrefs()
34 const {setColorMode, setDarkTheme} = useSetThemePrefs()
35
36 const onChangeAppearance = useCallback(
37 (keys: string[]) => {
38 const appearance = keys.find(key => key !== colorMode) as
39 | 'system'
40 | 'light'
41 | 'dark'
42 | undefined
43 if (!appearance) return
44 setColorMode(appearance)
45 },
46 [setColorMode, colorMode],
47 )
48
49 const onChangeDarkTheme = useCallback(
50 (keys: string[]) => {
51 const theme = keys.find(key => key !== darkTheme) as
52 | 'dim'
53 | 'dark'
54 | undefined
55 if (!theme) return
56 setDarkTheme(theme)
57 },
58 [setDarkTheme, darkTheme],
59 )
60
61 const onChangeFontFamily = useCallback(
62 (values: string[]) => {
63 const next = values[0] === 'system' ? 'system' : 'theme'
64 fonts.setFontFamily(next)
65 },
66 [fonts],
67 )
68
69 const onChangeFontScale = useCallback(
70 (values: string[]) => {
71 const next = values[0] || ('0' as any)
72 fonts.setFontScale(next)
73 },
74 [fonts],
75 )
76
77 const {currentAccount} = useSession()
78
79 return (
80 <LayoutAnimationConfig skipExiting skipEntering>
81 <Layout.Screen testID="preferencesThreadsScreen">
82 <Layout.Header.Outer>
83 <Layout.Header.BackButton />
84 <Layout.Header.Content>
85 <Layout.Header.TitleText>
86 <Trans>Appearance</Trans>
87 </Layout.Header.TitleText>
88 </Layout.Header.Content>
89 <Layout.Header.Slot />
90 </Layout.Header.Outer>
91 <Layout.Content>
92 <SettingsList.Container>
93 <AppearanceToggleButtonGroup
94 title={_(msg`Color mode`)}
95 icon={PhoneIcon}
96 items={[
97 {
98 label: _(msg`System`),
99 name: 'system',
100 },
101 {
102 label: _(msg`Light`),
103 name: 'light',
104 },
105 {
106 label: _(msg`Dark`),
107 name: 'dark',
108 },
109 ]}
110 values={[colorMode]}
111 onChange={onChangeAppearance}
112 />
113
114 {colorMode !== 'light' && (
115 <Animated.View
116 entering={native(FadeInUp)}
117 exiting={native(FadeOutUp)}>
118 <AppearanceToggleButtonGroup
119 title={_(msg`Dark theme`)}
120 icon={MoonIcon}
121 items={[
122 {
123 label: _(msg`Dim`),
124 name: 'dim',
125 },
126 {
127 label: _(msg`Dark`),
128 name: 'dark',
129 },
130 ]}
131 values={[darkTheme ?? 'dim']}
132 onChange={onChangeDarkTheme}
133 />
134 </Animated.View>
135 )}
136
137 <Animated.View layout={native(LinearTransition)}>
138 <SettingsList.Divider />
139
140 <AppearanceToggleButtonGroup
141 title={_(msg`Font`)}
142 description={_(
143 msg`For the best experience, we recommend using the theme font.`,
144 )}
145 icon={Aa}
146 items={[
147 {
148 label: _(msg`System`),
149 name: 'system',
150 },
151 {
152 label: _(msg`Theme`),
153 name: 'theme',
154 },
155 ]}
156 values={[fonts.family]}
157 onChange={onChangeFontFamily}
158 />
159
160 <AppearanceToggleButtonGroup
161 title={_(msg`Font size`)}
162 icon={TextSize}
163 items={[
164 {
165 label: _(msg`Smaller`),
166 name: '-1',
167 },
168 {
169 label: _(msg`Default`),
170 name: '0',
171 },
172 {
173 label: _(msg`Larger`),
174 name: '1',
175 },
176 ]}
177 values={[fonts.scale]}
178 onChange={onChangeFontScale}
179 />
180
181 {isNative && DISCOVER_DEBUG_DIDS[currentAccount?.did ?? ''] && (
182 <>
183 <SettingsList.Divider />
184
185 <SettingsList.LinkItem
186 to="/settings/app-icon"
187 label={_(msg`App Icon`)}>
188 <SettingsList.ItemIcon icon={Logo} />
189 <SettingsList.ItemText>
190 <Trans>App Icon</Trans>
191 </SettingsList.ItemText>
192 </SettingsList.LinkItem>
193 </>
194 )}
195 </Animated.View>
196 </SettingsList.Container>
197 </Layout.Content>
198 </Layout.Screen>
199 </LayoutAnimationConfig>
200 )
201}
202
203export function AppearanceToggleButtonGroup({
204 title,
205 description,
206 icon: Icon,
207 items,
208 values,
209 onChange,
210}: {
211 title: string
212 description?: string
213 icon: React.ComponentType<SVGIconProps>
214 items: {
215 label: string
216 name: string
217 }[]
218 values: string[]
219 onChange: (values: string[]) => void
220}) {
221 const t = useTheme()
222 return (
223 <>
224 <SettingsList.Group contentContainerStyle={[a.gap_sm]} iconInset={false}>
225 <SettingsList.ItemIcon icon={Icon} />
226 <SettingsList.ItemText>{title}</SettingsList.ItemText>
227 {description && (
228 <Text
229 style={[
230 a.text_sm,
231 a.leading_snug,
232 t.atoms.text_contrast_medium,
233 a.w_full,
234 ]}>
235 {description}
236 </Text>
237 )}
238 <ToggleButton.Group label={title} values={values} onChange={onChange}>
239 {items.map(item => (
240 <ToggleButton.Button
241 key={item.name}
242 label={item.label}
243 name={item.name}>
244 <ToggleButton.ButtonText>{item.label}</ToggleButton.ButtonText>
245 </ToggleButton.Button>
246 ))}
247 </ToggleButton.Group>
248 </SettingsList.Group>
249 </>
250 )
251}