From 11b09dc30b9646d4d0bfd2cb6316037486d9ac7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Such=C3=BD?= Date: Mon, 15 Jan 2024 11:55:43 +0100 Subject: [PATCH] fix(mobile): icons in Modal (#10637) --- .../icons/src/components/CryptoIcon.tsx | 3 +++ .../icons/src/components/FlagIcon.tsx | 3 +++ suite-common/icons/src/components/Icon.tsx | 5 ++++- .../icons/src/useRerenderOnAppState.ios.ts | 4 ++++ .../icons/src/useRerenderOnAppState.ts | 21 +++++++++++++++++++ 5 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 suite-common/icons/src/useRerenderOnAppState.ios.ts create mode 100644 suite-common/icons/src/useRerenderOnAppState.ts diff --git a/suite-common/icons/src/components/CryptoIcon.tsx b/suite-common/icons/src/components/CryptoIcon.tsx index 4b3be60c408..0d2e92f6f31 100644 --- a/suite-common/icons/src/components/CryptoIcon.tsx +++ b/suite-common/icons/src/components/CryptoIcon.tsx @@ -5,6 +5,7 @@ import { TokenAddress } from '@suite-common/wallet-types'; import { CryptoIconName, cryptoIcons } from '../icons'; import { genericTokenIcon, TokenIconName, tokenIcons } from '../tokenIcons'; +import { useRerenderOnAppStateChange } from '../useRerenderOnAppState'; export type CoinSymbol = CryptoIconName | TokenAddress; @@ -30,6 +31,8 @@ const getIconFile = (symbol: CoinSymbol) => { }; export const CryptoIcon = ({ symbol, size = 'small' }: CryptoIconProps) => { + useRerenderOnAppStateChange(); + const iconFile = getIconFile(symbol); const svg = useSVG(iconFile); const sizeNumber = cryptoIconSizes[size]; diff --git a/suite-common/icons/src/components/FlagIcon.tsx b/suite-common/icons/src/components/FlagIcon.tsx index fba18ac58b5..427286d0c85 100644 --- a/suite-common/icons/src/components/FlagIcon.tsx +++ b/suite-common/icons/src/components/FlagIcon.tsx @@ -1,6 +1,7 @@ import { Canvas, ImageSVG, useSVG } from '@shopify/react-native-skia'; import { FlagIconName, flagIcons } from '../icons'; +import { useRerenderOnAppStateChange } from '../useRerenderOnAppState'; type FlagIconProps = { name: FlagIconName; @@ -16,6 +17,8 @@ const flagIconSizes = { type FlagIconSize = keyof typeof flagIconSizes; export const FlagIcon = ({ name, size = 'medium' }: FlagIconProps) => { + useRerenderOnAppStateChange(); + const svg = useSVG(flagIcons[name]); const sizeNumber = flagIconSizes[size]; return ( diff --git a/suite-common/icons/src/components/Icon.tsx b/suite-common/icons/src/components/Icon.tsx index d44ebb586aa..1adc7fbfb38 100644 --- a/suite-common/icons/src/components/Icon.tsx +++ b/suite-common/icons/src/components/Icon.tsx @@ -6,6 +6,7 @@ import { useNativeStyles } from '@trezor/styles'; import { Color, Colors, CSSColor } from '@trezor/theme'; import { IconName, icons } from '../icons'; +import { useRerenderOnAppStateChange } from '../useRerenderOnAppState'; export type IconColor = 'svgSource' | Color | CSSColor | SharedValue; @@ -34,7 +35,7 @@ const isReanimatedSharedValue = (value: IconColor): value is SharedValue, themeColors: Color }; export const Icon = ({ name, customSize, size = 'large', color = 'iconDefault' }: IconProps) => { + useRerenderOnAppStateChange(); + const svg = useSVG(icons[name]); const { utils: { colors }, diff --git a/suite-common/icons/src/useRerenderOnAppState.ios.ts b/suite-common/icons/src/useRerenderOnAppState.ios.ts new file mode 100644 index 00000000000..df2cfd8dfb1 --- /dev/null +++ b/suite-common/icons/src/useRerenderOnAppState.ios.ts @@ -0,0 +1,4 @@ +/** + * Hotfix is not needed for iOS. + */ +export const useRerenderOnAppStateChange = () => {}; diff --git a/suite-common/icons/src/useRerenderOnAppState.ts b/suite-common/icons/src/useRerenderOnAppState.ts new file mode 100644 index 00000000000..cdbf12032da --- /dev/null +++ b/suite-common/icons/src/useRerenderOnAppState.ts @@ -0,0 +1,21 @@ +import { useEffect, useState } from 'react'; +import { AppState } from 'react-native'; + +/** + * It's necessary because Skia Android bug when Icon components are used in Modal component. + * After app is suspended to background and then resumed, icons are not rendered. + * This is a workaround for this issue. + * @see https://github.com/Shopify/react-native-skia/issues/2135 + */ +export const useRerenderOnAppStateChange = () => { + const [_, setRerender] = useState(0); + + useEffect(() => { + const handleChange = () => { + setRerender(prev => prev + 1); + }; + const subscription = AppState.addEventListener('change', handleChange); + + return () => subscription.remove(); + }, []); +};