diff --git a/.imgbotconfig b/.imgbotconfig
index da6942061dd1..81d9a16614f9 100644
--- a/.imgbotconfig
+++ b/.imgbotconfig
@@ -1,7 +1,3 @@
{
- "ignoredFiles": [
- "assets/images/themeDependent/empty-state_background-fade-dark.png", // Caused an issue with color gradients, https://github.com/Expensify/App/issues/30499
- "assets/images/themeDependent/empty-state_background-fade-light.png"
- ],
"aggressiveCompression": "false"
}
diff --git a/assets/images/themeDependent/empty-state_background-fade-dark.png b/assets/images/themeDependent/empty-state_background-fade-dark.png
deleted file mode 100644
index 59951ef707fb..000000000000
Binary files a/assets/images/themeDependent/empty-state_background-fade-dark.png and /dev/null differ
diff --git a/assets/images/themeDependent/empty-state_background-fade-dark.svg b/assets/images/themeDependent/empty-state_background-fade-dark.svg
new file mode 100644
index 000000000000..1e4a7eb6b396
--- /dev/null
+++ b/assets/images/themeDependent/empty-state_background-fade-dark.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/themeDependent/empty-state_background-fade-light.png b/assets/images/themeDependent/empty-state_background-fade-light.png
deleted file mode 100644
index 200996057b47..000000000000
Binary files a/assets/images/themeDependent/empty-state_background-fade-light.png and /dev/null differ
diff --git a/assets/images/themeDependent/empty-state_background-fade-light.svg b/assets/images/themeDependent/empty-state_background-fade-light.svg
new file mode 100644
index 000000000000..31cd9533ff71
--- /dev/null
+++ b/assets/images/themeDependent/empty-state_background-fade-light.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/App.tsx b/src/App.tsx
index 1bf42a54d2a5..8aee808c219a 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -27,6 +27,7 @@ import SafeArea from './components/SafeArea';
import ScrollOffsetContextProvider from './components/ScrollOffsetContextProvider';
import {SearchRouterContextProvider} from './components/Search/SearchRouter/SearchRouterContext';
import SidePanelContextProvider from './components/SidePanel/SidePanelContextProvider';
+import SVGDefinitionsProvider from './components/SVGDefinitionsProvider';
import ThemeIllustrationsProvider from './components/ThemeIllustrationsProvider';
import ThemeProvider from './components/ThemeProvider';
import ThemeStylesProvider from './components/ThemeStylesProvider';
@@ -91,6 +92,7 @@ function App() {
ThemeProvider,
ThemeStylesProvider,
ThemeIllustrationsProvider,
+ SVGDefinitionsProvider,
HTMLEngineProvider,
PortalProvider,
SafeArea,
diff --git a/src/components/SVGDefinitionsProvider/LinearGradientEmptyStateBackground.tsx b/src/components/SVGDefinitionsProvider/LinearGradientEmptyStateBackground.tsx
new file mode 100644
index 000000000000..f8d119af6d39
--- /dev/null
+++ b/src/components/SVGDefinitionsProvider/LinearGradientEmptyStateBackground.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+
+type LinearGradientEmptyStateBackgroundProps = {
+ isDarkTheme?: boolean;
+};
+
+/**
+ * Global definitions for @assets/images/themeDependent/empty-state_background-fade
+ */
+function LinearGradientEmptyStateBackground({isDarkTheme}: LinearGradientEmptyStateBackgroundProps) {
+ const stopColor1 = isDarkTheme ? '#1a3d32' : '#e6e1da';
+ const stopColor2 = isDarkTheme ? '#061b09' : '#fcfbf9';
+ return (
+
+
+
+
+ );
+}
+
+export default LinearGradientEmptyStateBackground;
diff --git a/src/components/SVGDefinitionsProvider/index.native.tsx b/src/components/SVGDefinitionsProvider/index.native.tsx
new file mode 100644
index 000000000000..1ac4aa191cfb
--- /dev/null
+++ b/src/components/SVGDefinitionsProvider/index.native.tsx
@@ -0,0 +1,7 @@
+import type ChildrenProps from '@src/types/utils/ChildrenProps';
+
+function SVGDefinitionsProvider({children}: ChildrenProps) {
+ return children;
+}
+
+export default SVGDefinitionsProvider;
diff --git a/src/components/SVGDefinitionsProvider/index.tsx b/src/components/SVGDefinitionsProvider/index.tsx
new file mode 100644
index 000000000000..fce526b28ecd
--- /dev/null
+++ b/src/components/SVGDefinitionsProvider/index.tsx
@@ -0,0 +1,28 @@
+import type {ReactElement} from 'react';
+import React from 'react';
+import type ChildrenProps from '@src/types/utils/ChildrenProps';
+import LinearGradientEmptyStateBackground from './LinearGradientEmptyStateBackground';
+
+/**
+ * Provides global SVG definitions and helps avoid duplicated ids.
+ * Duplicated ids in the cause rendering issues (like missing gradients).
+ */
+function SVGDefinitionsProvider({children}: ChildrenProps): ReactElement | null {
+ return (
+ <>
+
+ {children}
+ >
+ );
+}
+
+SVGDefinitionsProvider.displayName = 'SVGDefinitionsProvider';
+export default SVGDefinitionsProvider;
diff --git a/src/pages/home/report/AnimatedEmptyStateBackground.tsx b/src/pages/home/report/AnimatedEmptyStateBackground.tsx
index 2c0dca03e54e..bba88933f164 100644
--- a/src/pages/home/report/AnimatedEmptyStateBackground.tsx
+++ b/src/pages/home/report/AnimatedEmptyStateBackground.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import {View} from 'react-native';
-import Animated, {clamp, SensorType, useAnimatedSensor, useAnimatedStyle, useReducedMotion, useSharedValue, withSpring} from 'react-native-reanimated';
+import Animated, {clamp, FadeIn, SensorType, useAnimatedSensor, useAnimatedStyle, useReducedMotion, useSharedValue, withSpring} from 'react-native-reanimated';
+import ImageSVG from '@components/ImageSVG';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useStyleUtils from '@hooks/useStyleUtils';
import useThemeIllustrations from '@hooks/useThemeIllustrations';
@@ -21,8 +22,10 @@ function AnimatedEmptyStateBackground() {
const {windowWidth} = useWindowDimensions();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const illustrations = useThemeIllustrations();
+ const illustrationWidth = CONST.EMPTY_STATE_BACKGROUND.ASPECT_RATIO * CONST.EMPTY_STATE_BACKGROUND.WIDE_SCREEN.IMAGE_HEIGHT; // or whatever your SVG's natural width is
+ const maxBackgroundWidth = variables.sideBarWidth + illustrationWidth;
// If window width is greater than the max background width, repeat the background image
- const maxBackgroundWidth = variables.sideBarWidth + CONST.EMPTY_STATE_BACKGROUND.ASPECT_RATIO * CONST.EMPTY_STATE_BACKGROUND.WIDE_SCREEN.IMAGE_HEIGHT;
+ const numberOfRepeats = windowWidth > maxBackgroundWidth ? Math.ceil(windowWidth / illustrationWidth) : 1;
// Get data from phone rotation sensor and prep other variables for animation
const animatedSensor = useAnimatedSensor(SensorType.GYROSCOPE);
@@ -50,11 +53,21 @@ function AnimatedEmptyStateBackground() {
return (
- maxBackgroundWidth ? 'repeat' : 'cover'}
- />
+ entering={FadeIn}
+ >
+ {Array.from({length: numberOfRepeats}).map((_, index) => (
+ 1 ? illustrationWidth : undefined}
+ style={{position: 'absolute', left: index * illustrationWidth}}
+ preserveAspectRatio="xMidYMid slice"
+ />
+ ))}
+
);
}
diff --git a/src/styles/theme/illustrations/themes/dark.ts b/src/styles/theme/illustrations/themes/dark.ts
index 300440960862..c3aa477846e9 100644
--- a/src/styles/theme/illustrations/themes/dark.ts
+++ b/src/styles/theme/illustrations/themes/dark.ts
@@ -2,7 +2,7 @@ import GenericCompanyCard from '@assets/images/companyCards/generic-dark.svg';
import GenericCSVCompanyCardLarge from '@assets/images/companyCards/large/generic-csv-dark-large.svg';
import GenericCompanyCardLarge from '@assets/images/companyCards/large/generic-dark-large.svg';
import ExpensifyApprovedLogo from '@assets/images/subscription-details__approvedlogo.svg';
-import EmptyStateBackgroundImage from '@assets/images/themeDependent/empty-state_background-fade-dark.png';
+import EmptyStateBackgroundImage from '@assets/images/themeDependent/empty-state_background-fade-dark.svg';
import ExampleCheckEN from '@assets/images/themeDependent/example-check-image-dark-en.png';
import ExampleCheckES from '@assets/images/themeDependent/example-check-image-dark-es.png';
import WorkspaceProfile from '@assets/images/workspace-profile.png';
diff --git a/src/styles/theme/illustrations/themes/light.ts b/src/styles/theme/illustrations/themes/light.ts
index a4fa86d2f816..2cabb18b18b1 100644
--- a/src/styles/theme/illustrations/themes/light.ts
+++ b/src/styles/theme/illustrations/themes/light.ts
@@ -2,7 +2,7 @@ import GenericCompanyCard from '@assets/images/companyCards/generic-light.svg';
import GenericCSVCompanyCardLarge from '@assets/images/companyCards/large/generic-csv-light-large.svg';
import GenericCompanyCardLarge from '@assets/images/companyCards/large/generic-light-large.svg';
import ExpensifyApprovedLogo from '@assets/images/subscription-details__approvedlogo--light.svg';
-import EmptyStateBackgroundImage from '@assets/images/themeDependent/empty-state_background-fade-light.png';
+import EmptyStateBackgroundImage from '@assets/images/themeDependent/empty-state_background-fade-light.svg';
import ExampleCheckEN from '@assets/images/themeDependent/example-check-image-light-en.png';
import ExampleCheckES from '@assets/images/themeDependent/example-check-image-light-es.png';
import WorkspaceProfile from '@assets/images/workspace-profile-light.png';
diff --git a/src/styles/theme/illustrations/types.ts b/src/styles/theme/illustrations/types.ts
index cd897fa4d1cc..72cbc2b55090 100644
--- a/src/styles/theme/illustrations/types.ts
+++ b/src/styles/theme/illustrations/types.ts
@@ -4,7 +4,7 @@ import type {SvgProps} from 'react-native-svg';
import type IconAsset from '@src/types/utils/IconAsset';
type IllustrationsType = {
- EmptyStateBackgroundImage: ImageSourcePropType;
+ EmptyStateBackgroundImage: FC;
ExampleCheckES: ImageSourcePropType;
ExampleCheckEN: ImageSourcePropType;
WorkspaceProfile: ImageSourcePropType;
diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts
index 3bb7c50fa39d..4b3cfaebbde2 100644
--- a/src/styles/utils/index.ts
+++ b/src/styles/utils/index.ts
@@ -861,7 +861,7 @@ function getHorizontalStackedOverlayAvatarStyle(oneAvatarSize: AvatarSize, oneAv
/**
* Gets the correct size for the empty state background image based on screen dimensions
*/
-function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): ImageStyle {
+function getReportWelcomeBackgroundImageStyle(isSmallScreenWidth: boolean): ViewStyle {
if (isSmallScreenWidth) {
return {
position: 'absolute',