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`] = `
[
-
- ,
+ ,
- ,
+ ,
- ,
+ ,
- ,
+ ,
- ,
+ ,
- ,
+ ,
- ,
+ ,
- ,
+ ,
-
@@ -174,26 +159,10 @@ exports[`SendScene2 1 spendTarget 1`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -1346,26 +1300,10 @@ exports[`SendScene2 1 spendTarget with info tiles 1`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -2706,26 +2629,10 @@ exports[`SendScene2 2 spendTargets 1`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -4037,26 +3929,10 @@ exports[`SendScene2 2 spendTargets hide tiles 1`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -5213,26 +5074,10 @@ exports[`SendScene2 2 spendTargets hide tiles 2`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -6365,26 +6195,10 @@ exports[`SendScene2 2 spendTargets hide tiles 3`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -7362,26 +7161,10 @@ exports[`SendScene2 2 spendTargets lock tiles 1`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -8652,26 +8420,10 @@ exports[`SendScene2 2 spendTargets lock tiles 2`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -9877,26 +9614,10 @@ exports[`SendScene2 2 spendTargets lock tiles 3`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -11061,26 +10767,10 @@ exports[`SendScene2 Render SendScene 1`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
- ,
+ ,
- ,
+ ,
-
@@ -174,26 +159,10 @@ exports[`TransactionDetailsScene should render 1`] = `
}
r="238.7781690140845"
/>
-
- ,
+ ,
-
@@ -1923,26 +1877,10 @@ exports[`TransactionDetailsScene should render with negative nativeAmount and fi
}
r="238.7781690140845"
/>
-
- ,
+ ,
{
...DefaultTheme,
colors: {
...DefaultTheme.colors,
- background: theme.background.color
+ background: theme.backgroundGradientColors[0]
}
}
}, [theme])
diff --git a/src/components/buttons/MinimalButton.tsx b/src/components/buttons/MinimalButton.tsx
index 288350f63ab..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.background.color
+ color: theme.secondaryButtonText
},
labelDisabled: {
...labelCommon,
diff --git a/src/components/common/SceneWrapper.tsx b/src/components/common/SceneWrapper.tsx
index 26ef4b7b6da..3e667c34327 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 { OverrideDots } 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,12 +38,17 @@ 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
+ // 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
@@ -55,6 +61,9 @@ interface SceneWrapperProps {
// Settings for when using ScrollView
keyboardShouldPersistTaps?: 'always' | 'never' | 'handled'
+ // Override existing background dots parameters
+ overrideDots?: OverrideDots
+
// Padding to add inside the scene border:
padding?: number
@@ -80,8 +89,12 @@ interface SceneWrapperProps {
*/
export function SceneWrapper(props: SceneWrapperProps): JSX.Element {
const {
- accentColor,
+ overrideDots,
+ accentColors,
avoidKeyboard = false,
+ backgroundGradientColors,
+ backgroundGradientStart,
+ backgroundGradientEnd,
children,
renderDrawer,
hasHeader = true,
@@ -152,7 +165,13 @@ export function SceneWrapper(props: SceneWrapperProps): JSX.Element {
return (
-
+
{}
@@ -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 (
-
+
>(FlashList)
@@ -60,8 +60,6 @@ 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 backgroundGradientColor = iconColor == null ? transparentBackground : `${iconColor}44`
// Selectors:
const exchangeDenom = useSelector(state => getExchangeDenomination(state, pluginId, currencyCode))
@@ -270,16 +268,32 @@ function TransactionListComponent(props: Props) {
[handleChangeText, handleDoneSearching, handleStartSearching, isSearching, searchText]
)
+ 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 }) => (
<>
-
+
-
+
)
}
diff --git a/src/hooks/useIconColor.ts b/src/hooks/useIconColor.ts
new file mode 100644
index 00000000000..e39365c9245
--- /dev/null
+++ b/src/hooks/useIconColor.ts
@@ -0,0 +1,40 @@
+import * as React from 'react'
+import { getColors } from 'react-native-image-colors'
+
+import { useState } from '../types/reactHooks'
+import { EdgeAsset } from '../types/types'
+import { getCurrencyIconUris } from '../util/CdnUris'
+
+export const useIconColor = (edgeAsset: EdgeAsset): string | undefined => {
+ 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
+}
diff --git a/src/theme/variables/edgeDark.ts b/src/theme/variables/edgeDark.ts
index a0f7d693717..43f3e4feef4 100644
--- a/src/theme/variables/edgeDark.ts
+++ b/src/theme/variables/edgeDark.ts
@@ -132,14 +132,15 @@ export const edgeDark: Theme = {
loadingIcon: palette.edgeMint,
// Background
- 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: [
{
// Top-left:
- accent: 'keep',
color: palette.white,
cx: '10%',
cy: '10%',
@@ -154,14 +155,18 @@ export const edgeDark: Theme = {
},
{
// Bottom-left:
- accent: 'drop',
color: palette.backgroundGreen,
cx: '-15%',
cy: '100%',
r: scale(220)
}
- ]
+ ],
+ 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 44a03901cc7..ad483dd9a63 100644
--- a/src/theme/variables/edgeLight.ts
+++ b/src/theme/variables/edgeLight.ts
@@ -126,15 +126,22 @@ export const edgeLight: Theme = {
loadingIcon: palette.edgeBlue,
// Background
- 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) },
- { color: palette.backgroundPurple, cx: '25%', cy: '75%', r: scale(150), accent: 'keep' }
- ]
+ { color: palette.backgroundPurple, cx: '25%', cy: '75%', r: scale(150) }
+ ],
+ 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 fc5c69cedcd..132f3fc9226 100644
--- a/src/theme/variables/testDark.ts
+++ b/src/theme/variables/testDark.ts
@@ -132,14 +132,15 @@ export const testDark: Theme = {
loadingIcon: palette.edgeMint,
// Background
- 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: [
{
// Top-left:
- accent: 'keep',
color: palette.white,
cx: '10%',
cy: '10%',
@@ -154,14 +155,18 @@ export const testDark: Theme = {
},
{
// Bottom-left:
- accent: 'drop',
color: palette.backgroundGreen,
cx: '-15%',
cy: '100%',
r: scale(220)
}
- ]
+ ],
+ 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 79c4159916f..73b904c81a9 100644
--- a/src/theme/variables/testLight.ts
+++ b/src/theme/variables/testLight.ts
@@ -126,15 +126,22 @@ export const testLight: Theme = {
loadingIcon: palette.edgeBlue,
// Background
- 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) },
{ color: palette.backgroundPurple, cx: '25%', cy: '75%', r: scale(150) }
- ]
+ ],
+ 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 96dc5320e2a..f930eeeaa03 100644
--- a/src/types/Theme.ts
+++ b/src/types/Theme.ts
@@ -1,18 +1,20 @@
import { asNumber, asObject } from 'cleaners'
+import { AccentColors } from '../components/ui4/DotsBackground'
+
export type ImageProp = { uri: string } | number
export interface ThemeDot {
- // The accent color will override the dot color when it is present.
- // To change this behavior, use 'keep' to preserve the dot color or
- // 'drop' to remove the dot completely:
- accent?: 'keep' | 'drop'
+ accentColor?: keyof AccentColors
color: string
cx: number | string
cy: number | string
r: number
}
+// Updates to dots. undefined keeps the dots, null deletes them
+export type OverrideDots = Array | undefined | null>
+
interface ThemeGradientParams {
colors: string[]
start: GradientCoords
@@ -96,12 +98,19 @@ export interface Theme {
loadingIcon: string
// Background
- background: {
+ backgroundGradientColors: string[]
+ backgroundGradientStart: { x: number; y: number }
+ backgroundGradientEnd: { x: number; y: number }
+ backgroundDots: {
blurRadius: number
- color: string // Never include an alpha here
dotOpacity: number
dots: ThemeDot[]
+ assetOverrideDots: OverrideDots
}
+ 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
+}