diff --git a/app.config.js b/app.config.js index 149b949e6f..fd754a50f7 100644 --- a/app.config.js +++ b/app.config.js @@ -71,7 +71,7 @@ module.exports = function (config) { policy: 'appVersion', }, orientation: 'portrait', - icon: './assets/icon.png', + icon: './assets/app-icons/ios_icon_default_light.png', userInterfaceStyle: 'automatic', splash: SPLASH_CONFIG, // hsl(211, 99%, 53%), same as palette.default.brandText @@ -162,7 +162,7 @@ module.exports = function (config) { backgroundColor: DARK_SPLASH_CONFIG_ANDROID.backgroundColor, }, android: { - icon: './assets/icon.png', + icon: './assets/app-icons/android_icon_default_light.png', adaptiveIcon: { foregroundImage: './assets/icon-android-foreground.png', monochromeImage: './assets/icon-android-foreground.png', @@ -273,6 +273,73 @@ module.exports = function (config) { ], }, ], + [ + '@mozzius/expo-dynamic-app-icon', + { + /** + * Default set + */ + default_light: { + ios: './assets/app-icons/ios_icon_default_light.png', + android: './assets/app-icons/android_icon_default_light.png', + prerendered: true, + }, + default_dark: { + ios: './assets/app-icons/ios_icon_default_dark.png', + android: './assets/app-icons/android_icon_default_dark.png', + prerendered: true, + }, + + /** + * Bluesky+ core set + */ + core_aurora: { + ios: './assets/app-icons/ios_icon_core_aurora.png', + android: './assets/app-icons/android_icon_core_aurora.png', + prerendered: true, + }, + core_bonfire: { + ios: './assets/app-icons/ios_icon_core_bonfire.png', + android: './assets/app-icons/android_icon_core_bonfire.png', + prerendered: true, + }, + core_sunrise: { + ios: './assets/app-icons/ios_icon_core_sunrise.png', + android: './assets/app-icons/android_icon_core_sunrise.png', + prerendered: true, + }, + core_sunset: { + ios: './assets/app-icons/ios_icon_core_sunset.png', + android: './assets/app-icons/android_icon_core_sunset.png', + prerendered: true, + }, + core_midnight: { + ios: './assets/app-icons/ios_icon_core_midnight.png', + android: './assets/app-icons/android_icon_core_midnight.png', + prerendered: true, + }, + core_flat_blue: { + ios: './assets/app-icons/ios_icon_core_flat_blue.png', + android: './assets/app-icons/android_icon_core_flat_blue.png', + prerendered: true, + }, + core_flat_white: { + ios: './assets/app-icons/ios_icon_core_flat_white.png', + android: './assets/app-icons/android_icon_core_flat_white.png', + prerendered: true, + }, + core_flat_black: { + ios: './assets/app-icons/ios_icon_core_flat_black.png', + android: './assets/app-icons/android_icon_core_flat_black.png', + prerendered: true, + }, + core_classic: { + ios: './assets/app-icons/ios_icon_core_classic.png', + android: './assets/app-icons/android_icon_core_classic.png', + prerendered: true, + }, + }, + ], ].filter(Boolean), extra: { eas: { diff --git a/assets/app-icons/android_icon_core_aurora.png b/assets/app-icons/android_icon_core_aurora.png new file mode 100644 index 0000000000..52ce4045b5 Binary files /dev/null and b/assets/app-icons/android_icon_core_aurora.png differ diff --git a/assets/app-icons/android_icon_core_bonfire.png b/assets/app-icons/android_icon_core_bonfire.png new file mode 100644 index 0000000000..18ed29112a Binary files /dev/null and b/assets/app-icons/android_icon_core_bonfire.png differ diff --git a/assets/app-icons/android_icon_core_classic.png b/assets/app-icons/android_icon_core_classic.png new file mode 100644 index 0000000000..56fed7f160 Binary files /dev/null and b/assets/app-icons/android_icon_core_classic.png differ diff --git a/assets/app-icons/android_icon_core_flat_black.png b/assets/app-icons/android_icon_core_flat_black.png new file mode 100644 index 0000000000..f203145e7e Binary files /dev/null and b/assets/app-icons/android_icon_core_flat_black.png differ diff --git a/assets/app-icons/android_icon_core_flat_blue.png b/assets/app-icons/android_icon_core_flat_blue.png new file mode 100644 index 0000000000..4a4d157a96 Binary files /dev/null and b/assets/app-icons/android_icon_core_flat_blue.png differ diff --git a/assets/app-icons/android_icon_core_flat_white.png b/assets/app-icons/android_icon_core_flat_white.png new file mode 100644 index 0000000000..a6dc31d55e Binary files /dev/null and b/assets/app-icons/android_icon_core_flat_white.png differ diff --git a/assets/app-icons/android_icon_core_midnight.png b/assets/app-icons/android_icon_core_midnight.png new file mode 100644 index 0000000000..a5fdd1e701 Binary files /dev/null and b/assets/app-icons/android_icon_core_midnight.png differ diff --git a/assets/app-icons/android_icon_core_sunrise.png b/assets/app-icons/android_icon_core_sunrise.png new file mode 100644 index 0000000000..2fbded7e7e Binary files /dev/null and b/assets/app-icons/android_icon_core_sunrise.png differ diff --git a/assets/app-icons/android_icon_core_sunset.png b/assets/app-icons/android_icon_core_sunset.png new file mode 100644 index 0000000000..d632a23229 Binary files /dev/null and b/assets/app-icons/android_icon_core_sunset.png differ diff --git a/assets/app-icons/android_icon_default_dark.png b/assets/app-icons/android_icon_default_dark.png new file mode 100644 index 0000000000..b968147e59 Binary files /dev/null and b/assets/app-icons/android_icon_default_dark.png differ diff --git a/assets/app-icons/android_icon_default_light.png b/assets/app-icons/android_icon_default_light.png new file mode 100644 index 0000000000..4ceaec6045 Binary files /dev/null and b/assets/app-icons/android_icon_default_light.png differ diff --git a/assets/app-icons/ios_icon_core_aurora.png b/assets/app-icons/ios_icon_core_aurora.png new file mode 100644 index 0000000000..3be24585d7 Binary files /dev/null and b/assets/app-icons/ios_icon_core_aurora.png differ diff --git a/assets/app-icons/ios_icon_core_bonfire.png b/assets/app-icons/ios_icon_core_bonfire.png new file mode 100644 index 0000000000..0928f5eb78 Binary files /dev/null and b/assets/app-icons/ios_icon_core_bonfire.png differ diff --git a/assets/app-icons/ios_icon_core_classic.png b/assets/app-icons/ios_icon_core_classic.png new file mode 100644 index 0000000000..56fed7f160 Binary files /dev/null and b/assets/app-icons/ios_icon_core_classic.png differ diff --git a/assets/app-icons/ios_icon_core_flat_black.png b/assets/app-icons/ios_icon_core_flat_black.png new file mode 100644 index 0000000000..69e7ccfeb0 Binary files /dev/null and b/assets/app-icons/ios_icon_core_flat_black.png differ diff --git a/assets/app-icons/ios_icon_core_flat_blue.png b/assets/app-icons/ios_icon_core_flat_blue.png new file mode 100644 index 0000000000..71a380265e Binary files /dev/null and b/assets/app-icons/ios_icon_core_flat_blue.png differ diff --git a/assets/app-icons/ios_icon_core_flat_white.png b/assets/app-icons/ios_icon_core_flat_white.png new file mode 100644 index 0000000000..2f9f615362 Binary files /dev/null and b/assets/app-icons/ios_icon_core_flat_white.png differ diff --git a/assets/app-icons/ios_icon_core_midnight.png b/assets/app-icons/ios_icon_core_midnight.png new file mode 100644 index 0000000000..65a882ad39 Binary files /dev/null and b/assets/app-icons/ios_icon_core_midnight.png differ diff --git a/assets/app-icons/ios_icon_core_sunrise.png b/assets/app-icons/ios_icon_core_sunrise.png new file mode 100644 index 0000000000..9405c7ab3e Binary files /dev/null and b/assets/app-icons/ios_icon_core_sunrise.png differ diff --git a/assets/app-icons/ios_icon_core_sunset.png b/assets/app-icons/ios_icon_core_sunset.png new file mode 100644 index 0000000000..ac57f6b010 Binary files /dev/null and b/assets/app-icons/ios_icon_core_sunset.png differ diff --git a/assets/app-icons/ios_icon_default_dark.png b/assets/app-icons/ios_icon_default_dark.png new file mode 100644 index 0000000000..b9accd048d Binary files /dev/null and b/assets/app-icons/ios_icon_default_dark.png differ diff --git a/assets/app-icons/ios_icon_default_light.png b/assets/app-icons/ios_icon_default_light.png new file mode 100644 index 0000000000..200641fae3 Binary files /dev/null and b/assets/app-icons/ios_icon_default_light.png differ diff --git a/assets/icon.png b/assets/icon.png deleted file mode 100644 index 75866fc827..0000000000 Binary files a/assets/icon.png and /dev/null differ diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index d0d6b4c494..19252f7654 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -257,6 +257,7 @@ func serve(cctx *cli.Context) error { e.GET("/settings/privacy-and-security", server.WebGeneric) e.GET("/settings/content-and-media", server.WebGeneric) e.GET("/settings/about", server.WebGeneric) + e.GET("/settings/app-icon", server.WebGeneric) e.GET("/sys/debug", server.WebGeneric) e.GET("/sys/debug-mod", server.WebGeneric) e.GET("/sys/log", server.WebGeneric) diff --git a/package.json b/package.json index 6e7fcd9e86..ddb90c4f65 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "@lingui/react": "^4.5.0", "@mattermost/react-native-paste-input": "^0.7.1", "@miblanchard/react-native-slider": "^2.3.1", + "@mozzius/expo-dynamic-app-icon": "^1.4.1", "@radix-ui/react-dismissable-layer": "^1.1.1", "@radix-ui/react-dropdown-menu": "2.0.1", "@radix-ui/react-focus-guards": "^1.1.1", diff --git a/src/Navigation.tsx b/src/Navigation.tsx index cc815ef70b..0b162d3634 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, @@ -362,6 +363,14 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { requireAuth: true, }} /> + AppIconSettingsScreen} + options={{ + title: title(msg`App Icon`), + requireAuth: true, + }} + /> HashtagScreen} diff --git a/src/alf/atoms.ts b/src/alf/atoms.ts index fe8cf9a789..1f08eb7e1b 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 a44a9e5182..ebf4b1ee11 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -29,6 +29,7 @@ export const DISCOVER_DEBUG_DIDS: Record = { '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 41a47b4bc9..9e3407261d 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 388e8c5210..188665d849 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 0000000000..1dd87d45f4 --- /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 +export function AppIconSettingsScreen({}: Props) { + const {_} = useLingui() + const sets = useAppIconSets() + + return ( + + + + Defaults + + {sets.defaults.map(icon => ( + + AppIcon.setAppIcon(icon.id)}> + + + + {icon.name} + + + ))} + + + Bluesky+ + + {sets.core.map(icon => ( + + { + 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) + } + }}> + + + + {icon.name} + + + ))} + + + + ) +} + +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 d0beb7d506..662b3093f5 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 ( @@ -121,6 +126,8 @@ export function AppearanceSettingsScreen({}: Props) { )} + + + + {DISCOVER_DEBUG_DIDS[currentAccount?.did ?? ''] && ( + <> + + + + + + App Icon + + + + )} diff --git a/yarn.lock b/yarn.lock index 16dbba3974..f70489e179 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3736,7 +3736,7 @@ resolved "https://registry.yarnpkg.com/@expo/html-elements/-/html-elements-0.4.3.tgz#32b4ca05dd13582164ed1be34ae87e22adfd1d5b" integrity sha512-UwEEdnpyhUEIDe/AkFSBUmCuwcknjAuu73fd5L9Rm/BbHczYXCrtyZmzCNVBsAiHhwUjmhNWzFlr9cAkp/sxIA== -"@expo/image-utils@0.3.23": +"@expo/image-utils@0.3.23", "@expo/image-utils@^0.3.23": version "0.3.23" resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.3.23.tgz#f14fd7e1f5ff6f8e4911a41e27dd274470665c3f" integrity sha512-nhUVvW0TrRE4jtWzHQl8TR4ox7kcmrc2I0itaeJGjxF5A54uk7avgA0wRt7jP1rdvqQo1Ke1lXyLYREdhN9tPw== @@ -4937,6 +4937,15 @@ resolved "https://registry.yarnpkg.com/@miblanchard/react-native-slider/-/react-native-slider-2.3.1.tgz#79e0f1f9b1ce43ef25ee51ee9256c012e5dfa412" integrity sha512-J/hZDBWmXq8fJeOnTVHqIUVDHshqMSpJVxJ4WqwuCBKl5Rke9OBYXIdkSlgi75OgtScAr8FKK5KNkDKHUf6JIg== +"@mozzius/expo-dynamic-app-icon@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@mozzius/expo-dynamic-app-icon/-/expo-dynamic-app-icon-1.4.1.tgz#245e54c31347e3ec2a1ce10f0df8cf07a0c1be6e" + integrity sha512-IiL6OiuW4kP5Jz/vrZ6U1t0m4gK1rW5VZAQzszdVcZy1cadX3EdR2/uA6jMU0qSwuesk028RhO6S0uBI9ckxBw== + dependencies: + "@expo/image-utils" "^0.3.23" + expo-modules-core "^1.0.3" + xcode "^3.0.1" + "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" @@ -10663,6 +10672,13 @@ expo-modules-core@1.12.11: dependencies: invariant "^2.2.4" +expo-modules-core@^1.0.3: + version "1.12.26" + resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-1.12.26.tgz#86c4087dc6246abfc4d7f5e61097dc8cc4b22262" + integrity sha512-y8yDWjOi+rQRdO+HY+LnUlz8qzHerUaw/LUjKPU/mX8PRXP4UUPEEp5fjAwBU44xjNmYSHWZDwet4IBBE+yQUA== + dependencies: + invariant "^2.2.4" + expo-navigation-bar@~3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/expo-navigation-bar/-/expo-navigation-bar-3.0.4.tgz#c122194f720f0fb03430fa8a34c15980fc0a169f"