diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Navigation.tsx | 9 | ||||
-rw-r--r-- | src/alf/atoms.ts | 8 | ||||
-rw-r--r-- | src/lib/constants.ts | 1 | ||||
-rw-r--r-- | src/lib/routes/types.ts | 1 | ||||
-rw-r--r-- | src/routes.ts | 1 | ||||
-rw-r--r-- | src/screens/Settings/AppIconSettings.tsx | 252 | ||||
-rw-r--r-- | src/screens/Settings/AppearanceSettings.tsx | 24 |
7 files changed, 294 insertions, 2 deletions
diff --git a/src/Navigation.tsx b/src/Navigation.tsx index cc815ef70..0b162d363 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -79,6 +79,7 @@ import {PostRepostedByScreen} from '#/screens/Post/PostRepostedBy' import {ProfileKnownFollowersScreen} from '#/screens/Profile/KnownFollowers' import {ProfileLabelerLikedByScreen} from '#/screens/Profile/ProfileLabelerLikedBy' import {AppearanceSettingsScreen} from '#/screens/Settings/AppearanceSettings' +import {AppIconSettingsScreen} from '#/screens/Settings/AppIconSettings' import {NotificationSettingsScreen} from '#/screens/Settings/NotificationSettings' import { StarterPackScreen, @@ -363,6 +364,14 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { }} /> <Stack.Screen + name="AppIconSettings" + getComponent={() => AppIconSettingsScreen} + options={{ + title: title(msg`App Icon`), + requireAuth: true, + }} + /> + <Stack.Screen name="Hashtag" getComponent={() => HashtagScreen} options={{title: title(msg`Hashtag`)}} diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index fe8cf9a78..1f08eb7e1 100644 --- a/src/alf/atoms.ts +++ b/src/alf/atoms.ts @@ -1,7 +1,7 @@ import {Platform, StyleProp, StyleSheet, ViewStyle} from 'react-native' import * as tokens from '#/alf/tokens' -import {native, web} from '#/alf/util/platform' +import {ios, native, web} from '#/alf/util/platform' export const atoms = { debug: { @@ -312,6 +312,12 @@ export const atoms = { border_r: { borderRightWidth: StyleSheet.hairlineWidth, }, + curve_circular: ios({ + borderCurve: 'circular', + }), + curve_continuous: ios({ + borderCurve: 'continuous', + }), /* * Shadow diff --git a/src/lib/constants.ts b/src/lib/constants.ts index a44a9e518..ebf4b1ee1 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -29,6 +29,7 @@ export const DISCOVER_DEBUG_DIDS: Record<string, true> = { 'did:plc:p2cp5gopk7mgjegy6wadk3ep': true, // samuel.bsky.team 'did:plc:ragtjsm2j2vknwkz3zp4oxrd': true, // pfrazee.com 'did:plc:vpkhqolt662uhesyj6nxm7ys': true, // why.bsky.team + 'did:plc:3jpt2mvvsumj2r7eqk4gzzjz': true, // esb.lol } const BASE_FEEDBACK_FORM_URL = `${HELP_DESK_URL}/requests/new` diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 41a47b4bc..9e3407261 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -44,6 +44,7 @@ export type CommonNavigatorParams = { PrivacyAndSecuritySettings: undefined ContentAndMediaSettings: undefined AboutSettings: undefined + AppIconSettings: 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 388e8c521..188665d84 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -44,6 +44,7 @@ export const router = new Router({ PrivacyAndSecuritySettings: '/settings/privacy-and-security', ContentAndMediaSettings: '/settings/content-and-media', AboutSettings: '/settings/about', + AppIconSettings: '/settings/app-icon', // support Support: '/support', PrivacyPolicy: '/support/privacy', diff --git a/src/screens/Settings/AppIconSettings.tsx b/src/screens/Settings/AppIconSettings.tsx new file mode 100644 index 000000000..1dd87d45f --- /dev/null +++ b/src/screens/Settings/AppIconSettings.tsx @@ -0,0 +1,252 @@ +import React from 'react' +import {Alert, View} from 'react-native' +import {Image} from 'expo-image' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import * as AppIcon from '@mozzius/expo-dynamic-app-icon' +import {NativeStackScreenProps} from '@react-navigation/native-stack' + +import {PressableScale} from '#/lib/custom-animations/PressableScale' +import {CommonNavigatorParams} from '#/lib/routes/types' +import {isAndroid} from '#/platform/detection' +import {atoms as a, platform} from '#/alf' +import * as Layout from '#/components/Layout' +import {Text} from '#/components/Typography' + +type Props = NativeStackScreenProps<CommonNavigatorParams, 'AppIconSettings'> +export function AppIconSettingsScreen({}: Props) { + const {_} = useLingui() + const sets = useAppIconSets() + + return ( + <Layout.Screen> + <Layout.Header title={_('App Icon')} /> + <Layout.Content + contentContainerStyle={[a.py_2xl, a.px_xl, {paddingBottom: 100}]}> + <Text style={[a.text_lg, a.font_heavy]}>Defaults</Text> + <View style={[a.flex_row, a.flex_wrap]}> + {sets.defaults.map(icon => ( + <View + style={[{width: '50%'}, a.py_lg, a.px_xs, a.align_center]} + key={icon.id}> + <PressableScale + accessibilityLabel={icon.name} + accessibilityHint={_(msg`Tap to change app icon`)} + targetScale={0.95} + onPress={() => AppIcon.setAppIcon(icon.id)}> + <Image + source={platform({ + ios: icon.iosImage(), + android: icon.androidImage(), + })} + style={[ + {width: 100, height: 100}, + platform({ + ios: {borderRadius: 20}, + android: a.rounded_full, + }), + a.curve_continuous, + ]} + accessibilityIgnoresInvertColors + /> + </PressableScale> + <Text style={[a.text_center, a.font_bold, a.text_md, a.mt_md]}> + {icon.name} + </Text> + </View> + ))} + </View> + + <Text style={[a.text_lg, a.font_heavy]}>Bluesky+</Text> + <View style={[a.flex_row, a.flex_wrap]}> + {sets.core.map(icon => ( + <View + style={[{width: '50%'}, a.py_lg, a.px_xs, a.align_center]} + key={icon.id}> + <PressableScale + accessibilityLabel={icon.name} + accessibilityHint={_(msg`Tap to change app icon`)} + targetScale={0.95} + onPress={() => { + if (isAndroid) { + Alert.alert( + _(msg`Change app icon to "${icon.name}"`), + _(msg`The app will be restarted`), + [ + { + text: _(msg`Cancel`), + style: 'cancel', + }, + { + text: _(msg`OK`), + onPress: () => { + AppIcon.setAppIcon(icon.id) + }, + style: 'default', + }, + ], + ) + } else { + AppIcon.setAppIcon(icon.id) + } + }}> + <Image + source={platform({ + ios: icon.iosImage(), + android: icon.androidImage(), + })} + style={[ + {width: 100, height: 100}, + platform({ + ios: {borderRadius: 20}, + android: a.rounded_full, + }), + a.curve_continuous, + a.shadow_lg, + ]} + accessibilityIgnoresInvertColors + /> + </PressableScale> + <Text + style={[a.text_center, a.font_bold, a.text_md, a.mt_md]} + // for Classicâ„¢ + emoji> + {icon.name} + </Text> + </View> + ))} + </View> + </Layout.Content> + </Layout.Screen> + ) +} + +function useAppIconSets() { + const {_} = useLingui() + + return React.useMemo(() => { + const defaults = [ + { + id: 'default_light', + name: _('Light'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_default_light.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_default_light.png`) + }, + }, + { + id: 'default_dark', + name: _('Dark'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_default_dark.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_default_dark.png`) + }, + }, + ] + + /** + * Bluesky+ + */ + const core = [ + { + id: 'core_aurora', + name: _('Aurora'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_core_aurora.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_core_aurora.png`) + }, + }, + // { + // id: 'core_bonfire', + // name: _('Bonfire'), + // iosImage: () => { + // return require(`../../../assets/app-icons/ios_icon_core_bonfire.png`) + // }, + // androidImage: () => { + // return require(`../../../assets/app-icons/android_icon_core_bonfire.png`) + // }, + // }, + { + id: 'core_sunrise', + name: _('Sunrise'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_core_sunrise.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_core_sunrise.png`) + }, + }, + { + id: 'core_sunset', + name: _('Sunset'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_core_sunset.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_core_sunset.png`) + }, + }, + { + id: 'core_midnight', + name: _('Midnight'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_core_midnight.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_core_midnight.png`) + }, + }, + { + id: 'core_flat_blue', + name: _('Flat Blue'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_core_flat_blue.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_core_flat_blue.png`) + }, + }, + { + id: 'core_flat_white', + name: _('Flat White'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_core_flat_white.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_core_flat_white.png`) + }, + }, + { + id: 'core_flat_black', + name: _('Flat Black'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_core_flat_black.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_core_flat_black.png`) + }, + }, + { + id: 'core_classic', + name: _('Bluesky Classicâ„¢'), + iosImage: () => { + return require(`../../../assets/app-icons/ios_icon_core_classic.png`) + }, + androidImage: () => { + return require(`../../../assets/app-icons/android_icon_core_classic.png`) + }, + }, + ] + + return { + defaults, + core, + } + }, [_]) +} diff --git a/src/screens/Settings/AppearanceSettings.tsx b/src/screens/Settings/AppearanceSettings.tsx index d0beb7d50..662b3093f 100644 --- a/src/screens/Settings/AppearanceSettings.tsx +++ b/src/screens/Settings/AppearanceSettings.tsx @@ -5,11 +5,14 @@ import Animated, { LayoutAnimationConfig, LinearTransition, } from 'react-native-reanimated' -import {msg} from '@lingui/macro' +import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {DISCOVER_DEBUG_DIDS} from '#/lib/constants' import {CommonNavigatorParams, NativeStackScreenProps} from '#/lib/routes/types' +import {useSession} from '#/state/session' import {useSetThemePrefs, useThemePrefs} from '#/state/shell' +import {Logo} from '#/view/icons/Logo' import {atoms as a, native, useAlf, useTheme} from '#/alf' import * as ToggleButton from '#/components/forms/ToggleButton' import {Props as SVGIconProps} from '#/components/icons/common' @@ -70,6 +73,8 @@ export function AppearanceSettingsScreen({}: Props) { [fonts], ) + const {currentAccount} = useSession() + return ( <LayoutAnimationConfig skipExiting skipEntering> <Layout.Screen testID="preferencesThreadsScreen"> @@ -121,6 +126,8 @@ export function AppearanceSettingsScreen({}: Props) { )} <Animated.View layout={native(LinearTransition)}> + <SettingsList.Divider /> + <AppearanceToggleButtonGroup title={_(msg`Font`)} description={_( @@ -161,6 +168,21 @@ export function AppearanceSettingsScreen({}: Props) { values={[fonts.scale]} onChange={onChangeFontScale} /> + + {DISCOVER_DEBUG_DIDS[currentAccount?.did ?? ''] && ( + <> + <SettingsList.Divider /> + + <SettingsList.LinkItem + to="/settings/app-icon" + label={_(msg`App Icon`)}> + <SettingsList.ItemIcon icon={Logo} /> + <SettingsList.ItemText> + <Trans>App Icon</Trans> + </SettingsList.ItemText> + </SettingsList.LinkItem> + </> + )} </Animated.View> </SettingsList.Container> </Layout.Content> |