diff --git a/assets/icons/moon_stroke2_corner2_rounded.svg b/assets/icons/moon_stroke2_corner2_rounded.svg
new file mode 100644
index 0000000000..8f5c03699b
--- /dev/null
+++ b/assets/icons/moon_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/phone_stroke2_corner2_rounded.svg b/assets/icons/phone_stroke2_corner2_rounded.svg
new file mode 100644
index 0000000000..4f44f08e52
--- /dev/null
+++ b/assets/icons/phone_stroke2_corner2_rounded.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go
index 61a524a70b..8da291fe56 100644
--- a/bskyweb/cmd/bskyweb/server.go
+++ b/bskyweb/cmd/bskyweb/server.go
@@ -211,6 +211,7 @@ func serve(cctx *cli.Context) error {
e.GET("/settings/threads", server.WebGeneric)
e.GET("/settings/external-embeds", server.WebGeneric)
e.GET("/settings/accessibility", server.WebGeneric)
+ e.GET("/settings/appearance", server.WebGeneric)
e.GET("/sys/debug", server.WebGeneric)
e.GET("/sys/debug-mod", server.WebGeneric)
e.GET("/sys/log", server.WebGeneric)
diff --git a/src/Navigation.tsx b/src/Navigation.tsx
index 8646577c8b..79856879c3 100644
--- a/src/Navigation.tsx
+++ b/src/Navigation.tsx
@@ -44,6 +44,7 @@ import HashtagScreen from '#/screens/Hashtag'
import {ModerationScreen} from '#/screens/Moderation'
import {ProfileKnownFollowersScreen} from '#/screens/Profile/KnownFollowers'
import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy'
+import {AppearanceSettingsScreen} from '#/screens/Settings/AppearanceSettings'
import {
StarterPackScreen,
StarterPackScreenShort,
@@ -310,6 +311,14 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) {
requireAuth: true,
}}
/>
+ AppearanceSettingsScreen}
+ options={{
+ title: title(msg`Appearance Settings`),
+ requireAuth: true,
+ }}
+ />
HashtagScreen}
diff --git a/src/components/forms/ToggleButton.tsx b/src/components/forms/ToggleButton.tsx
index 7528426380..f47a272b18 100644
--- a/src/components/forms/ToggleButton.tsx
+++ b/src/components/forms/ToggleButton.tsx
@@ -23,10 +23,10 @@ export function Group({children, multiple, ...props}: GroupProps) {
style={[
a.w_full,
a.flex_row,
- a.border,
a.rounded_sm,
a.overflow_hidden,
t.atoms.border_contrast_low,
+ {borderWidth: 1},
]}>
{children}
diff --git a/src/components/icons/Moon.tsx b/src/components/icons/Moon.tsx
new file mode 100644
index 0000000000..4994370b9e
--- /dev/null
+++ b/src/components/icons/Moon.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Moon_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M12.097 2.53a1 1 0 0 1-.041 1.07 6 6 0 0 0 8.345 8.344 1 1 0 0 1 1.563.908c-.434 5.122-4.728 9.144-9.962 9.144-5.522 0-9.998-4.476-9.998-9.998 0-5.234 4.021-9.528 9.144-9.962a1 1 0 0 1 .949.494ZM9.424 4.424a7.998 7.998 0 1 0 10.152 10.152A8 8 0 0 1 9.424 4.424Z',
+})
diff --git a/src/components/icons/Phone.tsx b/src/components/icons/Phone.tsx
new file mode 100644
index 0000000000..62000a1e5d
--- /dev/null
+++ b/src/components/icons/Phone.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Phone_Stroke2_Corner0_Rounded = createSinglePathSVG({
+ path: 'M5 4a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H8a3 3 0 0 1-3-3V4Zm3-1a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1H8Zm2 2a1 1 0 0 1 1-1h2a1 1 0 1 1 0 2h-2a1 1 0 0 1-1-1Z',
+})
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
index fbb66c9e9a..0cc83b475a 100644
--- a/src/lib/routes/types.ts
+++ b/src/lib/routes/types.ts
@@ -38,6 +38,7 @@ export type CommonNavigatorParams = {
PreferencesThreads: undefined
PreferencesExternalEmbeds: undefined
AccessibilitySettings: undefined
+ AppearanceSettings: undefined
Search: {q?: string}
Hashtag: {tag: string; author?: string}
MessagesConversation: {conversation: string; embed?: string}
diff --git a/src/routes.ts b/src/routes.ts
index ddf4fb39fa..c9e23e08c8 100644
--- a/src/routes.ts
+++ b/src/routes.ts
@@ -32,6 +32,7 @@ export const router = new Router({
PreferencesThreads: '/settings/threads',
PreferencesExternalEmbeds: '/settings/external-embeds',
AccessibilitySettings: '/settings/accessibility',
+ AppearanceSettings: '/settings/appearance',
SavedFeeds: '/settings/saved-feeds',
Support: '/support',
PrivacyPolicy: '/support/privacy',
diff --git a/src/screens/Settings/AppearanceSettings.tsx b/src/screens/Settings/AppearanceSettings.tsx
new file mode 100644
index 0000000000..00a04bbfb6
--- /dev/null
+++ b/src/screens/Settings/AppearanceSettings.tsx
@@ -0,0 +1,135 @@
+import React, {useCallback} from 'react'
+import {View} from 'react-native'
+import Animated, {
+ FadeInDown,
+ FadeOutDown,
+ LayoutAnimationConfig,
+} from 'react-native-reanimated'
+import {msg, Trans} from '@lingui/macro'
+import {useLingui} from '@lingui/react'
+
+import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
+import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types'
+import {s} from '#/lib/styles'
+import {useSetThemePrefs, useThemePrefs} from '#/state/shell'
+import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
+import {ScrollView} from '#/view/com/util/Views'
+import {atoms as a, native, useTheme} from '#/alf'
+import * as ToggleButton from '#/components/forms/ToggleButton'
+import {Moon_Stroke2_Corner0_Rounded as MoonIcon} from '#/components/icons/Moon'
+import {Phone_Stroke2_Corner0_Rounded as PhoneIcon} from '#/components/icons/Phone'
+import {Text} from '#/components/Typography'
+
+type Props = NativeStackScreenProps
+export function AppearanceSettingsScreen({}: Props) {
+ const {_} = useLingui()
+ const t = useTheme()
+ const {isTabletOrMobile} = useWebMediaQueries()
+
+ const {colorMode, darkTheme} = useThemePrefs()
+ const {setColorMode, setDarkTheme} = useSetThemePrefs()
+
+ const onChangeAppearance = useCallback(
+ (keys: string[]) => {
+ const appearance = keys.find(key => key !== colorMode) as
+ | 'system'
+ | 'light'
+ | 'dark'
+ | undefined
+ if (!appearance) return
+ setColorMode(appearance)
+ },
+ [setColorMode, colorMode],
+ )
+
+ const onChangeDarkTheme = useCallback(
+ (keys: string[]) => {
+ const theme = keys.find(key => key !== darkTheme) as
+ | 'dim'
+ | 'dark'
+ | undefined
+ if (!theme) return
+ setDarkTheme(theme)
+ },
+ [setDarkTheme, darkTheme],
+ )
+
+ return (
+
+
+
+
+
+
+ Appearance
+
+
+
+
+
+
+
+
+ Mode
+
+
+
+
+
+ System
+
+
+
+
+ Light
+
+
+
+
+ Dark
+
+
+
+ {colorMode !== 'light' && (
+
+
+
+
+ Dark theme
+
+
+
+
+
+
+ Dim
+
+
+
+
+ Dark
+
+
+
+
+ )}
+
+
+
+
+ )
+}
diff --git a/src/view/icons/index.tsx b/src/view/icons/index.tsx
index beb31eca4e..8b1655e6a8 100644
--- a/src/view/icons/index.tsx
+++ b/src/view/icons/index.tsx
@@ -77,6 +77,7 @@ import {faListUl} from '@fortawesome/free-solid-svg-icons/faListUl'
import {faLock} from '@fortawesome/free-solid-svg-icons/faLock'
import {faMagnifyingGlass} from '@fortawesome/free-solid-svg-icons/faMagnifyingGlass'
import {faNoteSticky} from '@fortawesome/free-solid-svg-icons/faNoteSticky'
+import {faPaintRoller} from '@fortawesome/free-solid-svg-icons/faPaintRoller'
import {faPause} from '@fortawesome/free-solid-svg-icons/faPause'
import {faPen} from '@fortawesome/free-solid-svg-icons/faPen'
import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib'
@@ -178,6 +179,7 @@ library.add(
faMagnifyingGlass,
faMessage,
faNoteSticky,
+ faPaintRoller,
faPaste,
faPause,
faPen,
diff --git a/src/view/screens/AccessibilitySettings.tsx b/src/view/screens/AccessibilitySettings.tsx
index abe1550762..2a4477532d 100644
--- a/src/view/screens/AccessibilitySettings.tsx
+++ b/src/view/screens/AccessibilitySettings.tsx
@@ -27,6 +27,7 @@ import {ToggleButton} from '#/view/com/util/forms/ToggleButton'
import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
import {Text} from '#/view/com/util/text/Text'
import {ScrollView} from '#/view/com/util/Views'
+import {atoms as a} from '#/alf'
type Props = NativeStackScreenProps<
CommonNavigatorParams,
@@ -61,10 +62,13 @@ export function AccessibilitySettingsScreen({}: Props) {
showBackButton={isTabletOrMobile}
style={[
pal.border,
- {borderBottomWidth: 1},
- !isMobile && {borderLeftWidth: 1, borderRightWidth: 1},
+ a.border_b,
+ !isMobile && {
+ borderLeftWidth: StyleSheet.hairlineWidth,
+ borderRightWidth: StyleSheet.hairlineWidth,
+ },
]}>
-
+
Accessibility Settings
diff --git a/src/view/screens/PreferencesExternalEmbeds.tsx b/src/view/screens/PreferencesExternalEmbeds.tsx
index 57ca5e7653..ade7a53d90 100644
--- a/src/view/screens/PreferencesExternalEmbeds.tsx
+++ b/src/view/screens/PreferencesExternalEmbeds.tsx
@@ -18,6 +18,7 @@ import {
useSetExternalEmbedPref,
} from 'state/preferences'
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
+import {atoms as a} from '#/alf'
import {SimpleViewHeader} from '../com/util/SimpleViewHeader'
import {Text} from '../com/util/text/Text'
import {ScrollView} from '../com/util/Views'
@@ -47,8 +48,8 @@ export function PreferencesExternalEmbeds({}: Props) {
contentContainerStyle={[pal.viewLight, {paddingBottom: 75}]}>
-
+ style={[pal.border, a.border_b]}>
+
External Media Preferences
diff --git a/src/view/screens/PreferencesFollowingFeed.tsx b/src/view/screens/PreferencesFollowingFeed.tsx
index 879c925fbf..daa2aba858 100644
--- a/src/view/screens/PreferencesFollowingFeed.tsx
+++ b/src/view/screens/PreferencesFollowingFeed.tsx
@@ -19,6 +19,7 @@ import {ToggleButton} from '#/view/com/util/forms/ToggleButton'
import {SimpleViewHeader} from '#/view/com/util/SimpleViewHeader'
import {Text} from '#/view/com/util/text/Text'
import {ScrollView} from '#/view/com/util/Views'
+import {atoms as a} from '#/alf'
function RepliesThresholdInput({
enabled,
@@ -99,8 +100,8 @@ export function PreferencesFollowingFeed({}: Props) {
contentContainerStyle={{paddingBottom: 75}}>
-
+ style={[pal.border, a.border_b]}>
+
Following Feed Preferences
diff --git a/src/view/screens/PreferencesThreads.tsx b/src/view/screens/PreferencesThreads.tsx
index 3b09f0abb5..4a311f91ce 100644
--- a/src/view/screens/PreferencesThreads.tsx
+++ b/src/view/screens/PreferencesThreads.tsx
@@ -45,8 +45,8 @@ export function PreferencesThreads({}: Props) {
contentContainerStyle={{paddingBottom: 75}}>
-
+ style={[pal.border, a.border_b]}>
+
Thread Preferences
diff --git a/src/view/screens/Settings/index.tsx b/src/view/screens/Settings/index.tsx
index db74d5c0d5..c33be7d542 100644
--- a/src/view/screens/Settings/index.tsx
+++ b/src/view/screens/Settings/index.tsx
@@ -31,12 +31,7 @@ import {useClearPreferencesMutation} from '#/state/queries/preferences'
import {RQKEY as RQKEY_PROFILE} from '#/state/queries/profile'
import {useProfileQuery} from '#/state/queries/profile'
import {SessionAccount, useSession, useSessionApi} from '#/state/session'
-import {
- useOnboardingDispatch,
- useSetMinimalShellMode,
- useSetThemePrefs,
- useThemePrefs,
-} from '#/state/shell'
+import {useOnboardingDispatch, useSetMinimalShellMode} from '#/state/shell'
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
import {useCloseAllActiveElements} from '#/state/util'
import {useAnalytics} from 'lib/analytics/analytics'
@@ -52,7 +47,6 @@ import {CommonNavigatorParams, NativeStackScreenProps} from 'lib/routes/types'
import {NavigationProp} from 'lib/routes/types'
import {colors, s} from 'lib/styles'
import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn'
-import {SelectableBtn} from 'view/com/util/forms/SelectableBtn'
import {ToggleButton} from 'view/com/util/forms/ToggleButton'
import {Link, TextLink} from 'view/com/util/Link'
import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader'
@@ -61,8 +55,7 @@ import * as Toast from 'view/com/util/Toast'
import {UserAvatar} from 'view/com/util/UserAvatar'
import {ScrollView} from 'view/com/util/Views'
import {DeactivateAccountDialog} from '#/screens/Settings/components/DeactivateAccountDialog'
-import {useTheme} from '#/alf'
-import {atoms as a} from '#/alf'
+import {atoms as a, useTheme} from '#/alf'
import {useDialogControl} from '#/components/Dialog'
import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings'
import {navigate, resetToTab} from '#/Navigation'
@@ -168,8 +161,6 @@ function SettingsAccountCard({
type Props = NativeStackScreenProps
export function SettingsScreen({}: Props) {
const queryClient = useQueryClient()
- const {colorMode, darkTheme} = useThemePrefs()
- const {setColorMode, setDarkTheme} = useSetThemePrefs()
const pal = usePalette('default')
const {_} = useLingui()
const setMinimalShellMode = useSetMinimalShellMode()
@@ -296,6 +287,10 @@ export function SettingsScreen({}: Props) {
navigation.navigate('AccessibilitySettings')
}, [navigation])
+ const onPressAppearanceSettings = React.useCallback(() => {
+ navigation.navigate('AppearanceSettings')
+ }, [navigation])
+
const onPressBirthday = React.useCallback(() => {
birthdayControl.open()
}, [birthdayControl])
@@ -436,63 +431,6 @@ export function SettingsScreen({}: Props) {
-
- Appearance
-
-
-
- setColorMode('system')}
- accessibilityHint={_(msg`Sets color theme to system setting`)}
- />
- setColorMode('light')}
- accessibilityHint={_(msg`Sets color theme to light`)}
- />
- setColorMode('dark')}
- accessibilityHint={_(msg`Sets color theme to dark`)}
- />
-
-
-
-
-
- {colorMode !== 'light' && (
- <>
-
- Dark Theme
-
-
-
- setDarkTheme('dim')}
- accessibilityHint={_(msg`Sets dark theme to the dim theme`)}
- />
- setDarkTheme('dark')}
- accessibilityHint={_(msg`Sets dark theme to the dark theme`)}
- />
-
-
-
- >
- )}
-
Basics
@@ -519,6 +457,27 @@ export function SettingsScreen({}: Props) {
Accessibility
+
+
+
+
+
+ Appearance
+
+