From 4bd2c0f08be6f8a789a6d70d47972feef5832f30 Mon Sep 17 00:00:00 2001 From: Paul V Puey Date: Wed, 10 Jan 2024 10:20:11 -0800 Subject: [PATCH 1/6] Rename theme.background -> backgroundDots --- src/components/Main.tsx | 2 +- src/components/buttons/MinimalButton.tsx | 2 +- src/components/scenes/TransactionListScene.tsx | 2 +- src/components/ui4/DotsBackground.tsx | 2 +- src/theme/variables/edgeDark.ts | 2 +- src/theme/variables/edgeLight.ts | 2 +- src/theme/variables/testDark.ts | 2 +- src/theme/variables/testLight.ts | 2 +- src/types/Theme.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/Main.tsx b/src/components/Main.tsx index 17557ff97c8..70f0c2f8abf 100644 --- a/src/components/Main.tsx +++ b/src/components/Main.tsx @@ -240,7 +240,7 @@ export const Main = () => { ...DefaultTheme, colors: { ...DefaultTheme.colors, - background: theme.background.color + background: theme.backgroundDots.color } } }, [theme]) diff --git a/src/components/buttons/MinimalButton.tsx b/src/components/buttons/MinimalButton.tsx index 288350f63ab..9f58a4d3dad 100644 --- a/src/components/buttons/MinimalButton.tsx +++ b/src/components/buttons/MinimalButton.tsx @@ -95,7 +95,7 @@ const getStyles = cacheStyles((theme: Theme) => { }, labelSelected: { ...labelCommon, - color: theme.background.color + color: theme.backgroundDots.color }, labelDisabled: { ...labelCommon, diff --git a/src/components/scenes/TransactionListScene.tsx b/src/components/scenes/TransactionListScene.tsx index eeff33cae9c..0c9611ebd5d 100644 --- a/src/components/scenes/TransactionListScene.tsx +++ b/src/components/scenes/TransactionListScene.tsx @@ -60,7 +60,7 @@ function TransactionListComponent(props: Props) { const [searchText, setSearchText] = React.useState('') const [assetStatuses, setAssetStatuses] = React.useState([]) const [iconColor, setIconColor] = React.useState() - const transparentBackground = `${theme.background.color}00` + const transparentBackground = `${theme.backgroundDots.color}00` const backgroundGradientColor = iconColor == null ? transparentBackground : `${iconColor}44` // Selectors: diff --git a/src/components/ui4/DotsBackground.tsx b/src/components/ui4/DotsBackground.tsx index 09d7fa23c4b..4338eff97f2 100644 --- a/src/components/ui4/DotsBackground.tsx +++ b/src/components/ui4/DotsBackground.tsx @@ -13,7 +13,7 @@ interface Props { export function DotsBackground(props: Props): JSX.Element { const { accentColor } = props const theme = useTheme() - const { blurRadius, color, dotOpacity, dots } = theme.background + const { blurRadius, color, dotOpacity, dots } = theme.backgroundDots const accentDots: ThemeDot[] = [] for (const dot of dots) { diff --git a/src/theme/variables/edgeDark.ts b/src/theme/variables/edgeDark.ts index a0f7d693717..f51de6f1af6 100644 --- a/src/theme/variables/edgeDark.ts +++ b/src/theme/variables/edgeDark.ts @@ -132,7 +132,7 @@ export const edgeDark: Theme = { loadingIcon: palette.edgeMint, // Background - background: { + backgroundDots: { blurRadius: scale(80), color: palette.backgroundBlack, dotOpacity: 0.25, diff --git a/src/theme/variables/edgeLight.ts b/src/theme/variables/edgeLight.ts index 44a03901cc7..5fc4849f755 100644 --- a/src/theme/variables/edgeLight.ts +++ b/src/theme/variables/edgeLight.ts @@ -126,7 +126,7 @@ export const edgeLight: Theme = { loadingIcon: palette.edgeBlue, // Background - background: { + backgroundDots: { blurRadius: scale(80), color: palette.lightestGray, dotOpacity: 0.3, diff --git a/src/theme/variables/testDark.ts b/src/theme/variables/testDark.ts index fc5c69cedcd..d999f1be2ad 100644 --- a/src/theme/variables/testDark.ts +++ b/src/theme/variables/testDark.ts @@ -132,7 +132,7 @@ export const testDark: Theme = { loadingIcon: palette.edgeMint, // Background - background: { + backgroundDots: { blurRadius: scale(80), color: palette.backgroundBlack, dotOpacity: 0.25, diff --git a/src/theme/variables/testLight.ts b/src/theme/variables/testLight.ts index 79c4159916f..6f93a36227b 100644 --- a/src/theme/variables/testLight.ts +++ b/src/theme/variables/testLight.ts @@ -126,7 +126,7 @@ export const testLight: Theme = { loadingIcon: palette.edgeBlue, // Background - background: { + backgroundDots: { blurRadius: scale(80), color: palette.lightestGray, dotOpacity: 0.3, diff --git a/src/types/Theme.ts b/src/types/Theme.ts index 96dc5320e2a..30995b03418 100644 --- a/src/types/Theme.ts +++ b/src/types/Theme.ts @@ -96,7 +96,7 @@ export interface Theme { loadingIcon: string // Background - background: { + backgroundDots: { blurRadius: number color: string // Never include an alpha here dotOpacity: number From b55dc0701fa25da15ece06395689287a80ea449b Mon Sep 17 00:00:00 2001 From: Paul Puey Date: Fri, 12 Jan 2024 17:07:59 -0800 Subject: [PATCH 2/6] Change accentColor to overrideDots This gives a more flexible API to modify any parameters of the dots on any screen. --- src/components/common/SceneWrapper.tsx | 15 ++++--- .../scenes/TransactionListScene.tsx | 16 +++++++- src/components/ui4/DotsBackground.tsx | 41 +++++++++++++++---- src/theme/variables/edgeDark.ts | 5 +-- src/theme/variables/edgeLight.ts | 5 ++- src/theme/variables/testDark.ts | 5 +-- src/theme/variables/testLight.ts | 3 +- src/types/Theme.ts | 13 +++--- 8 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/components/common/SceneWrapper.tsx b/src/components/common/SceneWrapper.tsx index 26ef4b7b6da..c098299717b 100644 --- a/src/components/common/SceneWrapper.tsx +++ b/src/components/common/SceneWrapper.tsx @@ -8,12 +8,13 @@ import { EdgeInsets, useSafeAreaFrame, useSafeAreaInsets } from 'react-native-sa import { useSceneDrawerState } from '../../state/SceneDrawerState' import { useSelector } from '../../types/reactRedux' import { NavigationBase } from '../../types/routerTypes' +import { UpdateDots } from '../../types/Theme' import { maybeComponent } from '../hoc/maybeComponent' import { NotificationView } from '../notification/NotificationView' import { useTheme } from '../services/ThemeContext' import { MAX_TAB_BAR_HEIGHT } from '../themed/MenuTabs' import { SceneDrawer } from '../themed/SceneDrawer' -import { DotsBackground } from '../ui4/DotsBackground' +import { AccentColors, DotsBackground } from '../ui4/DotsBackground' import { KeyboardTracker } from './KeyboardTracker' export interface InsetStyles { @@ -37,8 +38,8 @@ interface SceneWrapperProps { // to changes to the info. children: React.ReactNode | ((info: SceneWrapperInfo) => React.ReactNode) - // Adjusts the blurred dots background: - accentColor?: string + // Object specifying accent colors to use for DotsBackground + accentColors?: AccentColors // True if this scene should shrink to avoid the keyboard: avoidKeyboard?: boolean @@ -55,6 +56,9 @@ interface SceneWrapperProps { // Settings for when using ScrollView keyboardShouldPersistTaps?: 'always' | 'never' | 'handled' + // Override existing background dots parameters + overrideDots?: UpdateDots + // Padding to add inside the scene border: padding?: number @@ -80,7 +84,8 @@ interface SceneWrapperProps { */ export function SceneWrapper(props: SceneWrapperProps): JSX.Element { const { - accentColor, + overrideDots, + accentColors, avoidKeyboard = false, children, renderDrawer, @@ -152,7 +157,7 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element { return ( - + >(FlashList) @@ -270,8 +271,21 @@ function TransactionListComponent(props: Props) { [handleChangeText, handleDoneSearching, handleStartSearching, isSearching, searchText] ) + const accentColors: AccentColors = { + // Transparent fallback for while iconColor is loading + iconAccentColor: iconColor ?? '#00000000' + } + return ( - + {({ insetStyles }) => ( <> | undefined | null> + interface ThemeGradientParams { colors: string[] start: GradientCoords @@ -98,9 +100,10 @@ export interface Theme { // Background backgroundDots: { blurRadius: number - color: string // Never include an alpha here + color: string dotOpacity: number dots: ThemeDot[] + assetOverrideDots: UpdateDots } // Camera Overlay From 085bdd96be11fd1921b4a331662dea52e253cf18 Mon Sep 17 00:00:00 2001 From: Paul Puey Date: Wed, 10 Jan 2024 15:58:13 -0800 Subject: [PATCH 3/6] Reimplement background color as a gradient --- .../__snapshots__/CategoryModal.test.tsx.snap | 4 +- ...reateWalletAccountSetupScene.test.tsx.snap | 42 +- .../CreateWalletImportScene.test.tsx.snap | 42 +- ...reateWalletSelectCryptoScene.test.tsx.snap | 42 +- .../CreateWalletSelectFiatScene.test.tsx.snap | 42 +- .../CryptoExchangeQuoteScene.test.tsx.snap | 42 +- .../CurrencyNotificationScene.test.tsx.snap | 42 +- .../CurrencySettings.ui.test.tsx.snap | 42 +- .../EdgeLoginScene.test.tsx.snap | 42 +- .../__snapshots__/SendScene2.ui.test.tsx.snap | 420 ++++++++++++------ .../__snapshots__/SettingsScene.test.tsx.snap | 84 ++-- .../TransactionDetailsScene.test.tsx.snap | 84 ++-- src/components/Main.tsx | 2 +- src/components/buttons/MinimalButton.tsx | 2 +- .../scenes/TransactionListScene.tsx | 2 +- src/components/ui4/DotsBackground.tsx | 15 +- src/theme/variables/edgeDark.ts | 4 +- src/theme/variables/edgeLight.ts | 4 +- src/theme/variables/testDark.ts | 4 +- src/theme/variables/testLight.ts | 4 +- src/types/Theme.ts | 4 +- 21 files changed, 647 insertions(+), 322 deletions(-) diff --git a/src/__tests__/modals/__snapshots__/CategoryModal.test.tsx.snap b/src/__tests__/modals/__snapshots__/CategoryModal.test.tsx.snap index d737c15bbb0..fe77252cc5d 100644 --- a/src/__tests__/modals/__snapshots__/CategoryModal.test.tsx.snap +++ b/src/__tests__/modals/__snapshots__/CategoryModal.test.tsx.snap @@ -220,7 +220,7 @@ exports[`CategoryModal should render with a subcategory 1`] = ` numberOfLines={1} style={ { - "color": "#1a1a1a", + "color": "#FFFFFF", "fontFamily": "Quicksand-Bold", "fontSize": 14, "marginHorizontal": 7, @@ -1836,7 +1836,7 @@ exports[`CategoryModal should render with an empty subcategory 1`] = ` numberOfLines={1} style={ { - "color": "#1a1a1a", + "color": "#FFFFFF", "fontFamily": "Quicksand-Bold", "fontSize": 14, "marginHorizontal": 7, diff --git a/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap index 51f4515aa7b..bef4933e6dc 100644 --- a/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap +++ b/src/__tests__/scenes/__snapshots__/CreateWalletAccountSetupScene.test.tsx.snap @@ -2,22 +2,36 @@ exports[`CreateWalletAccountSelect renders 1`] = ` [ - - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , - , + , { ...DefaultTheme, colors: { ...DefaultTheme.colors, - background: theme.backgroundDots.color + background: theme.backgroundGradientColors[0] } } }, [theme]) diff --git a/src/components/buttons/MinimalButton.tsx b/src/components/buttons/MinimalButton.tsx index 9f58a4d3dad..dc61f436308 100644 --- a/src/components/buttons/MinimalButton.tsx +++ b/src/components/buttons/MinimalButton.tsx @@ -95,7 +95,7 @@ const getStyles = cacheStyles((theme: Theme) => { }, labelSelected: { ...labelCommon, - color: theme.backgroundDots.color + color: theme.secondaryButtonText }, labelDisabled: { ...labelCommon, diff --git a/src/components/scenes/TransactionListScene.tsx b/src/components/scenes/TransactionListScene.tsx index d8e93cb1c45..9ff352fcfdd 100644 --- a/src/components/scenes/TransactionListScene.tsx +++ b/src/components/scenes/TransactionListScene.tsx @@ -61,7 +61,7 @@ function TransactionListComponent(props: Props) { const [searchText, setSearchText] = React.useState('') const [assetStatuses, setAssetStatuses] = React.useState([]) const [iconColor, setIconColor] = React.useState() - const transparentBackground = `${theme.backgroundDots.color}00` + const transparentBackground = `${theme.backgroundGradientColors[0]}00` const backgroundGradientColor = iconColor == null ? transparentBackground : `${iconColor}44` // Selectors: diff --git a/src/components/ui4/DotsBackground.tsx b/src/components/ui4/DotsBackground.tsx index 8f6b1ec37ec..da8e34b9d7d 100644 --- a/src/components/ui4/DotsBackground.tsx +++ b/src/components/ui4/DotsBackground.tsx @@ -1,5 +1,6 @@ import * as React from 'react' -import { LayoutChangeEvent, StyleSheet, View } from 'react-native' +import { LayoutChangeEvent, StyleSheet } from 'react-native' +import LinearGradient from 'react-native-linear-gradient' import { Circle, Defs, G, RadialGradient, Stop, Svg } from 'react-native-svg' import { useHandler } from '../../hooks/useHandler' @@ -17,7 +18,7 @@ interface Props { export function DotsBackground(props: Props): JSX.Element { const { accentColors, overrideDots } = props const theme = useTheme() - const { blurRadius, color, dotOpacity, dots } = theme.backgroundDots + const { blurRadius, dotOpacity, dots } = theme.backgroundDots const accentDots: ThemeDot[] = [] for (let i = 0; i < dots.length; i++) { @@ -97,12 +98,18 @@ export function DotsBackground(props: Props): JSX.Element { } return ( - + {accentDots.map(renderGradient)} {accentDots.map(renderCircle)} - + ) } diff --git a/src/theme/variables/edgeDark.ts b/src/theme/variables/edgeDark.ts index 61bee2c0ae5..b72e5a3a7cc 100644 --- a/src/theme/variables/edgeDark.ts +++ b/src/theme/variables/edgeDark.ts @@ -132,9 +132,11 @@ export const edgeDark: Theme = { loadingIcon: palette.edgeMint, // Background + backgroundGradientColors: [palette.backgroundBlack, palette.backgroundBlack], + backgroundGradientStart: { x: 0, y: 0 }, + backgroundGradientEnd: { x: 1, y: 1 }, backgroundDots: { blurRadius: scale(80), - color: palette.backgroundBlack, dotOpacity: 0.25, dots: [ { diff --git a/src/theme/variables/edgeLight.ts b/src/theme/variables/edgeLight.ts index b276b58a49a..1654e0a9d9e 100644 --- a/src/theme/variables/edgeLight.ts +++ b/src/theme/variables/edgeLight.ts @@ -126,9 +126,11 @@ export const edgeLight: Theme = { loadingIcon: palette.edgeBlue, // Background + backgroundGradientColors: [palette.lightestGray, palette.lightestGray], + backgroundGradientStart: { x: 0, y: 0 }, + backgroundGradientEnd: { x: 1, y: 0 }, backgroundDots: { blurRadius: scale(80), - color: palette.lightestGray, dotOpacity: 0.3, dots: [ { color: palette.backgroundGreen, cx: '75%', cy: '25%', r: scale(175) }, diff --git a/src/theme/variables/testDark.ts b/src/theme/variables/testDark.ts index f011c37b9a4..ea9307b3eb0 100644 --- a/src/theme/variables/testDark.ts +++ b/src/theme/variables/testDark.ts @@ -132,9 +132,11 @@ export const testDark: Theme = { loadingIcon: palette.edgeMint, // Background + backgroundGradientColors: [palette.black, palette.black], + backgroundGradientStart: { x: 0, y: 0 }, + backgroundGradientEnd: { x: 1, y: 0 }, backgroundDots: { blurRadius: scale(80), - color: palette.backgroundBlack, dotOpacity: 0.25, dots: [ { diff --git a/src/theme/variables/testLight.ts b/src/theme/variables/testLight.ts index 4c6fa96875b..5a59448abe0 100644 --- a/src/theme/variables/testLight.ts +++ b/src/theme/variables/testLight.ts @@ -126,9 +126,11 @@ export const testLight: Theme = { loadingIcon: palette.edgeBlue, // Background + backgroundGradientColors: [palette.lightestGray, palette.lightestGray], + backgroundGradientStart: { x: 0, y: 0 }, + backgroundGradientEnd: { x: 1, y: 0 }, backgroundDots: { blurRadius: scale(80), - color: palette.lightestGray, dotOpacity: 0.3, dots: [ { color: palette.backgroundGreen, cx: '75%', cy: '25%', r: scale(175) }, diff --git a/src/types/Theme.ts b/src/types/Theme.ts index bd873b91d68..ada12ec2034 100644 --- a/src/types/Theme.ts +++ b/src/types/Theme.ts @@ -98,9 +98,11 @@ export interface Theme { loadingIcon: string // Background + backgroundGradientColors: string[] + backgroundGradientStart: { x: number; y: number } + backgroundGradientEnd: { x: number; y: number } backgroundDots: { blurRadius: number - color: string dotOpacity: number dots: ThemeDot[] assetOverrideDots: UpdateDots From 1960869eac3787f1cc4360a31dae8d04ff43c584 Mon Sep 17 00:00:00 2001 From: Paul Puey Date: Wed, 10 Jan 2024 16:07:41 -0800 Subject: [PATCH 4/6] Add background override This let's specific scenes override the background with a gradient of their choice. TransactionList overrides by taking the iconColor and scaling it down --- src/components/common/SceneWrapper.tsx | 16 +++++++- .../scenes/TransactionListScene.tsx | 22 +++++------ src/components/ui4/DotsBackground.tsx | 12 ++++-- src/theme/variables/edgeDark.ts | 4 ++ src/theme/variables/edgeLight.ts | 4 ++ src/theme/variables/testDark.ts | 4 ++ src/theme/variables/testLight.ts | 4 ++ src/types/Theme.ts | 4 ++ src/util/utils.ts | 37 +++++++++++++++++++ 9 files changed, 91 insertions(+), 16 deletions(-) diff --git a/src/components/common/SceneWrapper.tsx b/src/components/common/SceneWrapper.tsx index c098299717b..eab10d6ee17 100644 --- a/src/components/common/SceneWrapper.tsx +++ b/src/components/common/SceneWrapper.tsx @@ -44,6 +44,11 @@ interface SceneWrapperProps { // True if this scene should shrink to avoid the keyboard: avoidKeyboard?: boolean + // Optional backgroundGradient overrides + backgroundGradientColors?: string[] + backgroundGradientStart?: { x: number; y: number } + backgroundGradientEnd?: { x: number; y: number } + // True if this scene has a header (with back button & such): hasHeader?: boolean @@ -87,6 +92,9 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element { overrideDots, accentColors, avoidKeyboard = false, + backgroundGradientColors, + backgroundGradientStart, + backgroundGradientEnd, children, renderDrawer, hasHeader = true, @@ -157,7 +165,13 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element { return ( - + ([]) const [iconColor, setIconColor] = React.useState() - const transparentBackground = `${theme.backgroundGradientColors[0]}00` - const backgroundGradientColor = iconColor == null ? transparentBackground : `${iconColor}44` // Selectors: const exchangeDenom = useSelector(state => getExchangeDenomination(state, pluginId, currencyCode)) @@ -276,6 +273,12 @@ function TransactionListComponent(props: Props) { iconAccentColor: iconColor ?? '#00000000' } + const backgroundColors = [...theme.assetBackgroundGradientColors] + if (iconColor != null) { + const scaledColor = darkenHexColor(iconColor, theme.assetBackgroundColorScale) + backgroundColors[0] = scaledColor + } + return ( {({ insetStyles }) => ( <> - diff --git a/src/theme/variables/edgeDark.ts b/src/theme/variables/edgeDark.ts index b72e5a3a7cc..43f3e4feef4 100644 --- a/src/theme/variables/edgeDark.ts +++ b/src/theme/variables/edgeDark.ts @@ -163,6 +163,10 @@ export const edgeDark: Theme = { ], assetOverrideDots: [undefined, { accentColor: 'iconAccentColor' }, null] }, + assetBackgroundGradientColors: [palette.darkAqua, palette.backgroundBlack], + assetBackgroundGradientStart: { x: 0, y: 0 }, + assetBackgroundGradientEnd: { x: 0, y: 1 }, + assetBackgroundColorScale: 0.1, // Camera Overlay cameraOverlayColor: palette.black, diff --git a/src/theme/variables/edgeLight.ts b/src/theme/variables/edgeLight.ts index 1654e0a9d9e..ad483dd9a63 100644 --- a/src/theme/variables/edgeLight.ts +++ b/src/theme/variables/edgeLight.ts @@ -138,6 +138,10 @@ export const edgeLight: Theme = { ], assetOverrideDots: [undefined, { accentColor: 'iconAccentColor' }, null] }, + assetBackgroundGradientColors: [palette.lightestGray, palette.lightestGray], + assetBackgroundGradientStart: { x: 0, y: 0 }, + assetBackgroundGradientEnd: { x: 0, y: 1 }, + assetBackgroundColorScale: 0.3, // Camera Overlay cameraOverlayColor: palette.gray, diff --git a/src/theme/variables/testDark.ts b/src/theme/variables/testDark.ts index ea9307b3eb0..132f3fc9226 100644 --- a/src/theme/variables/testDark.ts +++ b/src/theme/variables/testDark.ts @@ -163,6 +163,10 @@ export const testDark: Theme = { ], assetOverrideDots: [undefined, { accentColor: 'iconAccentColor' }, null] }, + assetBackgroundGradientColors: [palette.darkAqua, palette.black], + assetBackgroundGradientStart: { x: 0, y: 0 }, + assetBackgroundGradientEnd: { x: 0, y: 1 }, + assetBackgroundColorScale: 0.3, // Camera Overlay cameraOverlayColor: palette.black, diff --git a/src/theme/variables/testLight.ts b/src/theme/variables/testLight.ts index 5a59448abe0..73b904c81a9 100644 --- a/src/theme/variables/testLight.ts +++ b/src/theme/variables/testLight.ts @@ -138,6 +138,10 @@ export const testLight: Theme = { ], assetOverrideDots: [undefined, { accentColor: 'iconAccentColor' }, null] }, + assetBackgroundGradientColors: [palette.lightestGray, palette.lightestGray], + assetBackgroundGradientStart: { x: 0, y: 0 }, + assetBackgroundGradientEnd: { x: 0, y: 1 }, + assetBackgroundColorScale: 0.3, // Camera Overlay cameraOverlayColor: palette.gray, diff --git a/src/types/Theme.ts b/src/types/Theme.ts index ada12ec2034..ba8ef2f34de 100644 --- a/src/types/Theme.ts +++ b/src/types/Theme.ts @@ -107,6 +107,10 @@ export interface Theme { dots: ThemeDot[] assetOverrideDots: UpdateDots } + assetBackgroundGradientColors: string[] + assetBackgroundGradientStart: { x: number; y: number } + assetBackgroundGradientEnd: { x: number; y: number } + assetBackgroundColorScale: number // Camera Overlay cameraOverlayColor: string diff --git a/src/util/utils.ts b/src/util/utils.ts index 714a0b93cbf..b4160e89077 100644 --- a/src/util/utils.ts +++ b/src/util/utils.ts @@ -595,3 +595,40 @@ export const base58ToUuid = (base58String: string): string => { const uuid = v4({ random: bytes }) return uuid } + +/** + * Darken a color by a scale factor. + * @param hexColor of format '#1f1f1f1f' + * @param scaleFactor 0-1 with 0 being black, 1 is unchanged + * @returns darkened hex color string + */ +export const darkenHexColor = (hexColor: string, scaleFactor: number): string => { + if (scaleFactor < 0 || scaleFactor > 1) throw new Error('scaleFactor must be between 0-1') + hexColor = hexColor.replace('#', '') + + // Check for short and long hexadecimal color codes + if (hexColor.length === 3) { + // Expand short color code (e.g., #abc to #aabbcc) + hexColor = hexColor + .split('') + .map(char => char + char) + .join('') + } else if (hexColor.length !== 6) { + throw new Error('Invalid hexadecimal color code') + } + + // Parse the hexadecimal values + const r = parseInt(hexColor.slice(0, 2), 16) + const g = parseInt(hexColor.slice(2, 4), 16) + const b = parseInt(hexColor.slice(4, 6), 16) + + // Multiply each color component by the scale factor + const scaledR = Math.round(r * scaleFactor) + const scaledG = Math.round(g * scaleFactor) + const scaledB = Math.round(b * scaleFactor) + + // Convert the scaled values back to hexadecimal + const scaledHexColor = `#${scaledR.toString(16).padStart(2, '0')}${scaledG.toString(16).padStart(2, '0')}${scaledB.toString(16).padStart(2, '0')}` + + return scaledHexColor +} From aada9f5f36233755bc77c770f06c93f6ccdad141 Mon Sep 17 00:00:00 2001 From: Paul Puey Date: Wed, 10 Jan 2024 17:07:58 -0800 Subject: [PATCH 5/6] Add iconcolor gradient to send, request, earn, and txdetails --- .../__snapshots__/RequestScene.test.tsx.snap | 32 + .../__snapshots__/SendScene2.ui.test.tsx.snap | 630 +++--------------- .../TransactionDetailsScene.test.tsx.snap | 126 +--- src/components/scenes/RequestScene.tsx | 51 +- src/components/scenes/SendScene2.tsx | 27 +- .../scenes/Staking/StakeOptionsScene.tsx | 24 +- .../scenes/TransactionDetailsScene.tsx | 31 +- src/hooks/useIconColor.ts | 40 ++ 8 files changed, 300 insertions(+), 661 deletions(-) create mode 100644 src/hooks/useIconColor.ts diff --git a/src/__tests__/scenes/__snapshots__/RequestScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/RequestScene.test.tsx.snap index 20daecd4eb3..bcd8bd14d7d 100644 --- a/src/__tests__/scenes/__snapshots__/RequestScene.test.tsx.snap +++ b/src/__tests__/scenes/__snapshots__/RequestScene.test.tsx.snap @@ -2,7 +2,39 @@ exports[`Request should render with loaded props 1`] = ` - @@ -188,22 +159,6 @@ exports[`SendScene2 1 spendTarget 1`] = ` } r="238.7781690140845" /> - @@ -1191,13 +1146,13 @@ exports[`SendScene2 1 spendTarget with info tiles 1`] = ` - @@ -1374,22 +1300,6 @@ exports[`SendScene2 1 spendTarget with info tiles 1`] = ` } r="238.7781690140845" /> - @@ -2565,13 +2475,13 @@ exports[`SendScene2 2 spendTargets 1`] = ` - @@ -2748,22 +2629,6 @@ exports[`SendScene2 2 spendTargets 1`] = ` } r="238.7781690140845" /> - @@ -3910,13 +3775,13 @@ exports[`SendScene2 2 spendTargets hide tiles 1`] = ` - @@ -4093,22 +3929,6 @@ exports[`SendScene2 2 spendTargets hide tiles 1`] = ` } r="238.7781690140845" /> - @@ -5100,13 +4920,13 @@ exports[`SendScene2 2 spendTargets hide tiles 2`] = ` - @@ -5283,22 +5074,6 @@ exports[`SendScene2 2 spendTargets hide tiles 2`] = ` } r="238.7781690140845" /> - @@ -6266,13 +6041,13 @@ exports[`SendScene2 2 spendTargets hide tiles 3`] = ` - @@ -6449,22 +6195,6 @@ exports[`SendScene2 2 spendTargets hide tiles 3`] = ` } r="238.7781690140845" /> - @@ -7277,13 +7007,13 @@ exports[`SendScene2 2 spendTargets lock tiles 1`] = ` - @@ -7460,22 +7161,6 @@ exports[`SendScene2 2 spendTargets lock tiles 1`] = ` } r="238.7781690140845" /> - @@ -8581,13 +8266,13 @@ exports[`SendScene2 2 spendTargets lock tiles 2`] = ` - @@ -8764,22 +8420,6 @@ exports[`SendScene2 2 spendTargets lock tiles 2`] = ` } r="238.7781690140845" /> - @@ -9820,13 +9460,13 @@ exports[`SendScene2 2 spendTargets lock tiles 3`] = ` - @@ -10003,22 +9614,6 @@ exports[`SendScene2 2 spendTargets lock tiles 3`] = ` } r="238.7781690140845" /> - @@ -11018,13 +10613,13 @@ exports[`SendScene2 Render SendScene 1`] = ` - @@ -11201,22 +10767,6 @@ exports[`SendScene2 Render SendScene 1`] = ` } r="238.7781690140845" /> - diff --git a/src/__tests__/scenes/__snapshots__/TransactionDetailsScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/TransactionDetailsScene.test.tsx.snap index 31d33c6a4f5..5a139b61241 100644 --- a/src/__tests__/scenes/__snapshots__/TransactionDetailsScene.test.tsx.snap +++ b/src/__tests__/scenes/__snapshots__/TransactionDetailsScene.test.tsx.snap @@ -5,13 +5,13 @@ exports[`TransactionDetailsScene should render 1`] = ` - @@ -188,22 +159,6 @@ exports[`TransactionDetailsScene should render 1`] = ` } r="238.7781690140845" /> - @@ -1768,13 +1723,13 @@ exports[`TransactionDetailsScene should render with negative nativeAmount and fi - @@ -1951,22 +1877,6 @@ exports[`TransactionDetailsScene should render with negative nativeAmount and fi } r="238.7781690140845" /> - diff --git a/src/components/scenes/RequestScene.tsx b/src/components/scenes/RequestScene.tsx index 4e2f3b967e7..83179ed55a1 100644 --- a/src/components/scenes/RequestScene.tsx +++ b/src/components/scenes/RequestScene.tsx @@ -12,17 +12,18 @@ import { toggleAccountBalanceVisibility } from '../../actions/LocalSettingsActio import { selectWalletToken } from '../../actions/WalletActions' import { Fontello } from '../../assets/vector' import { getSpecialCurrencyInfo, SPECIAL_CURRENCY_INFO } from '../../constants/WalletAndCurrencyConstants' +import { useIconColor } from '../../hooks/useIconColor' import { lstrings } from '../../locales/strings' import { getDisplayDenomination, getExchangeDenomination } from '../../selectors/DenominationSelectors' import { getExchangeRate } from '../../selectors/WalletSelectors' import { config } from '../../theme/appConfig' -import { connect } from '../../types/reactRedux' +import { connect, useSelector } from '../../types/reactRedux' import { EdgeSceneProps, NavigationBase } from '../../types/routerTypes' import { GuiCurrencyInfo } from '../../types/types' -import { getTokenIdForced, isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' +import { getTokenId, getTokenIdForced, isKeysOnlyPlugin } from '../../util/CurrencyInfoHelpers' import { getAvailableBalance, getWalletName } from '../../util/CurrencyWalletHelpers' import { triggerHaptic } from '../../util/haptic' -import { convertNativeToDenomination, truncateDecimals, zeroString } from '../../util/utils' +import { convertNativeToDenomination, darkenHexColor, truncateDecimals, zeroString } from '../../util/utils' import { EdgeAnim } from '../common/EdgeAnim' import { SceneWrapper } from '../common/SceneWrapper' import { AddressModal } from '../modals/AddressModal' @@ -41,6 +42,7 @@ import { MainButton } from '../themed/MainButton' import { SceneHeader } from '../themed/SceneHeader' import { ShareButtons } from '../themed/ShareButtons' import { CardUi4 } from '../ui4/CardUi4' +import { AccentColors } from '../ui4/DotsBackground' interface OwnProps extends EdgeSceneProps<'request'> {} @@ -64,6 +66,10 @@ interface CurrencyMinimumPopupState { [pluginId: string]: ModalState } +interface HookProps { + iconColor?: string +} + type Props = StateProps & DispatchProps & OwnProps & ThemeProps interface State { @@ -82,7 +88,7 @@ interface AddressInfo { const inputAccessoryViewID: string = 'cancelHeaderId' -export class RequestSceneComponent extends React.Component { +export class RequestSceneComponent extends React.Component { flipInputRef: React.RefObject unsubscribeAddressChanged: (() => void) | undefined @@ -286,7 +292,7 @@ export class RequestSceneComponent extends React.Component { } render() { - const { currencyCode, exchangeSecondaryToPrimaryRatio, wallet, primaryCurrencyInfo, theme } = this.props + const { currencyCode, exchangeSecondaryToPrimaryRatio, iconColor, wallet, primaryCurrencyInfo, theme } = this.props const styles = getStyles(theme) if (currencyCode == null || primaryCurrencyInfo == null || exchangeSecondaryToPrimaryRatio == null || wallet == null) { @@ -307,10 +313,28 @@ export class RequestSceneComponent extends React.Component { // Selected denomination const denomString = `1 ${primaryCurrencyInfo.displayDenomination.name}` + const accentColors: AccentColors = { + // Transparent fallback for while iconColor is loading + iconAccentColor: iconColor ?? '#00000000' + } + + const backgroundColors = [...theme.assetBackgroundGradientColors] + if (iconColor != null) { + const scaledColor = darkenHexColor(iconColor, theme.assetBackgroundColorScale) + backgroundColors[0] = scaledColor + } + return keysOnlyMode ? ( this.renderKeysOnlyMode() ) : ( - + {lstrings.fragment_request_subtitle} @@ -539,7 +563,7 @@ const getStyles = cacheStyles((theme: Theme) => ({ } })) -export const RequestScene = connect( +const RequestSceneConnected = connect( state => { const { account } = state.core const { currencyWallets } = account @@ -596,3 +620,16 @@ export const RequestScene = connect( } }) )(withTheme(RequestSceneComponent)) + +export const RequestScene = (props: OwnProps) => { + const account = useSelector(state => state.core.account) + const currencyCode = useSelector(state => state.ui.wallets.selectedCurrencyCode) + const walletId = useSelector(state => state.ui.wallets.selectedWalletId) + const wallet = account.currencyWallets[walletId] ?? {} + + const { pluginId = '' } = wallet.currencyInfo ?? {} + const tokenId = getTokenId(account, pluginId, currencyCode) + + const iconColor = useIconColor({ pluginId, tokenId: tokenId !== undefined ? tokenId : '' }) + return +} diff --git a/src/components/scenes/SendScene2.tsx b/src/components/scenes/SendScene2.tsx index 9deb04a21a7..f08b68a844a 100644 --- a/src/components/scenes/SendScene2.tsx +++ b/src/components/scenes/SendScene2.tsx @@ -24,6 +24,7 @@ import { useAsyncEffect } from '../../hooks/useAsyncEffect' import { useDisplayDenom } from '../../hooks/useDisplayDenom' import { useExchangeDenom } from '../../hooks/useExchangeDenom' import { useHandler } from '../../hooks/useHandler' +import { useIconColor } from '../../hooks/useIconColor' import { useMount } from '../../hooks/useMount' import { useUnmount } from '../../hooks/useUnmount' import { useWatch } from '../../hooks/useWatch' @@ -37,7 +38,7 @@ import { getCurrencyCode } from '../../util/CurrencyInfoHelpers' import { getWalletName } from '../../util/CurrencyWalletHelpers' import { addToFioAddressCache, checkRecordSendFee, FIO_FEE_EXCEEDS_SUPPLIED_MAXIMUM, FIO_NO_BUNDLED_ERR_CODE, recordSend } from '../../util/FioAddressUtils' import { logActivity } from '../../util/logger' -import { convertTransactionFeeToDisplayFee, DECIMAL_PRECISION, zeroString } from '../../util/utils' +import { convertTransactionFeeToDisplayFee, darkenHexColor, DECIMAL_PRECISION, zeroString } from '../../util/utils' import { getMemoError, getMemoLabel, getMemoTitle } from '../../util/validateMemos' import { SceneWrapper } from '../common/SceneWrapper' import { styled } from '../hoc/styled' @@ -59,6 +60,7 @@ import { EditableAmountTile } from '../tiles/EditableAmountTile' import { ErrorTile } from '../tiles/ErrorTile' import { AlertCardUi4 } from '../ui4/AlertCardUi4' import { CardUi4 } from '../ui4/CardUi4' +import { AccentColors } from '../ui4/DotsBackground' import { RowUi4 } from '../ui4/RowUi4' // TODO: Check contentPadding @@ -181,6 +183,7 @@ const SendComponent = (props: Props) => { const cryptoExchangeDenomination = useExchangeDenom(pluginId, currencyCode) const parentDisplayDenom = useDisplayDenom(pluginId, currencyWallets[walletId].currencyInfo.currencyCode) const parentExchangeDenom = useExchangeDenom(pluginId, currencyWallets[walletId].currencyInfo.currencyCode) + const iconColor = useIconColor({ pluginId, tokenId }) spendInfo.tokenId = tokenId @@ -994,8 +997,28 @@ const SendComponent = (props: Props) => { disableSlider = true disabledText = lstrings.spending_limits_enter_pin } + + const accentColors: AccentColors = { + // Transparent fallback for while iconColor is loading + iconAccentColor: iconColor ?? '#00000000' + } + + const backgroundColors = [...theme.assetBackgroundGradientColors] + if (iconColor != null) { + const scaledColor = darkenHexColor(iconColor, theme.assetBackgroundColorScale) + backgroundColors[0] = scaledColor + } + return ( - + {({ insetStyles }) => ( <> { wallet: EdgeCurrencyWallet @@ -40,6 +43,7 @@ const StakeOptionsSceneComponent = (props: Props) => { const account = useSelector(state => state.core.account) const pluginId = wallet?.currencyInfo.pluginId const tokenId = pluginId ? getTokenIdForced(account, pluginId, currencyCode) : null + const iconColor = useIconColor({ pluginId, tokenId }) // // Handlers @@ -76,8 +80,26 @@ const StakeOptionsSceneComponent = (props: Props) => { ) } + const accentColors: AccentColors = { + // Transparent fallback for while iconColor is loading + iconAccentColor: iconColor ?? '#00000000' + } + + const backgroundColors = [...theme.assetBackgroundGradientColors] + if (iconColor != null) { + const scaledColor = darkenHexColor(iconColor, theme.assetBackgroundColorScale) + backgroundColors[0] = scaledColor + } + return ( - + diff --git a/src/components/scenes/TransactionDetailsScene.tsx b/src/components/scenes/TransactionDetailsScene.tsx index 37bed2d36de..cab6125a56f 100644 --- a/src/components/scenes/TransactionDetailsScene.tsx +++ b/src/components/scenes/TransactionDetailsScene.tsx @@ -13,6 +13,7 @@ import { useContactThumbnail } from '../../hooks/redux/useContactThumbnail' import { displayFiatAmount } from '../../hooks/useFiatText' import { useHandler } from '../../hooks/useHandler' import { useHistoricalRate } from '../../hooks/useHistoricalRate' +import { useIconColor } from '../../hooks/useIconColor' import { useWatch } from '../../hooks/useWatch' import { toPercentString } from '../../locales/intl' import { lstrings } from '../../locales/strings' @@ -22,7 +23,7 @@ import { useSelector } from '../../types/reactRedux' import { EdgeSceneProps } from '../../types/routerTypes' import { getCurrencyCodeWithAccount } from '../../util/CurrencyInfoHelpers' import { matchJson } from '../../util/matchJson' -import { convertNativeToExchange } from '../../util/utils' +import { convertNativeToExchange, darkenHexColor } from '../../util/utils' import { getMemoTitle } from '../../util/validateMemos' import { EdgeAnim } from '../common/EdgeAnim' import { SceneWrapper } from '../common/SceneWrapper' @@ -37,6 +38,7 @@ import { EdgeText } from '../themed/EdgeText' import { AdvancedDetailsCard } from '../ui4/AdvancedDetailsCard' import { ButtonsViewUi4 } from '../ui4/ButtonsViewUi4' import { CardUi4 } from '../ui4/CardUi4' +import { AccentColors } from '../ui4/DotsBackground' import { RowUi4 } from '../ui4/RowUi4' import { SwapDetailsCard } from '../ui4/SwapDetailsCard' import { TxCryptoAmountRow } from '../ui4/TxCryptoAmountRow' @@ -53,12 +55,14 @@ export interface TransactionDetailsParams { const TransactionDetailsComponent = (props: Props) => { const { navigation, route, wallet } = props const { edgeTransaction: transaction, walletId } = route.params - const { currencyCode, metadata, nativeAmount, date, txid } = transaction + const { currencyCode, metadata, nativeAmount, date, txid, tokenId } = transaction const { currencyInfo } = wallet const theme = useTheme() const account = useSelector(state => state.core.account) const styles = getStyles(theme) + const iconColor = useIconColor({ pluginId: currencyInfo.pluginId, tokenId }) + // Choose a default category based on metadata or the txAction const { direction, iconPluginId, mergedData, savedData } = getTxActionDisplayInfo(transaction, account, wallet) @@ -294,8 +298,29 @@ const TransactionDetailsComponent = (props: Props) => { const categoriesText = formatCategory(splitCategory(localMetadata.category ?? undefined)) + const accentColors: AccentColors = { + // Transparent fallback for while iconColor is loading + iconAccentColor: iconColor ?? '#00000000' + } + + const backgroundColors = [...theme.assetBackgroundGradientColors] + if (iconColor != null) { + const scaledColor = darkenHexColor(iconColor, theme.assetBackgroundColorScale) + backgroundColors[0] = scaledColor + } + return ( - + { + const [color, setColor] = useState(undefined) + const primaryCurrencyIconUrl = React.useMemo(() => { + const { pluginId, tokenId } = edgeAsset + if (pluginId == null) return null + + // Get Currency Icon URI + const icon = getCurrencyIconUris(pluginId, tokenId) + return icon.symbolImage + }, [edgeAsset]) + + React.useEffect(() => { + if (primaryCurrencyIconUrl == null) return + + getColors(primaryCurrencyIconUrl, { + cache: true, + key: primaryCurrencyIconUrl + }) + .then(colors => { + if (colors.platform === 'ios') { + setColor(colors.primary) + } + if (colors.platform === 'android') { + setColor(colors.vibrant) + } + }) + .catch(err => { + console.warn(err) + }) + }, [primaryCurrencyIconUrl]) + + return color +} From a90214b6a48e342e0377d5b204a59c7057773a14 Mon Sep 17 00:00:00 2001 From: Samuel Holmes Date: Fri, 12 Jan 2024 18:45:54 -0800 Subject: [PATCH 6/6] Rename `UpdateDots` to `OverrideDots` This matches more nicely with all the variable/identifier names. --- src/components/common/SceneWrapper.tsx | 4 ++-- src/components/ui4/DotsBackground.tsx | 4 ++-- src/types/Theme.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/common/SceneWrapper.tsx b/src/components/common/SceneWrapper.tsx index eab10d6ee17..3e667c34327 100644 --- a/src/components/common/SceneWrapper.tsx +++ b/src/components/common/SceneWrapper.tsx @@ -8,7 +8,7 @@ import { EdgeInsets, useSafeAreaFrame, useSafeAreaInsets } from 'react-native-sa import { useSceneDrawerState } from '../../state/SceneDrawerState' import { useSelector } from '../../types/reactRedux' import { NavigationBase } from '../../types/routerTypes' -import { UpdateDots } from '../../types/Theme' +import { OverrideDots } from '../../types/Theme' import { maybeComponent } from '../hoc/maybeComponent' import { NotificationView } from '../notification/NotificationView' import { useTheme } from '../services/ThemeContext' @@ -62,7 +62,7 @@ interface SceneWrapperProps { keyboardShouldPersistTaps?: 'always' | 'never' | 'handled' // Override existing background dots parameters - overrideDots?: UpdateDots + overrideDots?: OverrideDots // Padding to add inside the scene border: padding?: number diff --git a/src/components/ui4/DotsBackground.tsx b/src/components/ui4/DotsBackground.tsx index 1c36615b2de..52bf0bd75ec 100644 --- a/src/components/ui4/DotsBackground.tsx +++ b/src/components/ui4/DotsBackground.tsx @@ -4,7 +4,7 @@ import LinearGradient from 'react-native-linear-gradient' import { Circle, Defs, G, RadialGradient, Stop, Svg } from 'react-native-svg' import { useHandler } from '../../hooks/useHandler' -import { ThemeDot, UpdateDots } from '../../types/Theme' +import { OverrideDots, ThemeDot } from '../../types/Theme' import { useTheme } from '../services/ThemeContext' export interface AccentColors { @@ -15,7 +15,7 @@ interface Props { backgroundGradientColors?: string[] backgroundGradientStart?: { x: number; y: number } backgroundGradientEnd?: { x: number; y: number } - overrideDots?: UpdateDots + overrideDots?: OverrideDots accentColors?: AccentColors } diff --git a/src/types/Theme.ts b/src/types/Theme.ts index ba8ef2f34de..f930eeeaa03 100644 --- a/src/types/Theme.ts +++ b/src/types/Theme.ts @@ -13,7 +13,7 @@ export interface ThemeDot { } // Updates to dots. undefined keeps the dots, null deletes them -export type UpdateDots = Array | undefined | null> +export type OverrideDots = Array | undefined | null> interface ThemeGradientParams { colors: string[] @@ -105,7 +105,7 @@ export interface Theme { blurRadius: number dotOpacity: number dots: ThemeDot[] - assetOverrideDots: UpdateDots + assetOverrideDots: OverrideDots } assetBackgroundGradientColors: string[] assetBackgroundGradientStart: { x: number; y: number }