Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .imgbotconfig
Original file line number Diff line number Diff line change
@@ -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"
}
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -91,6 +92,7 @@ function App() {
ThemeProvider,
ThemeStylesProvider,
ThemeIllustrationsProvider,
SVGDefinitionsProvider,
HTMLEngineProvider,
PortalProvider,
SafeArea,
Expand Down
Original file line number Diff line number Diff line change
@@ -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 (
<linearGradient
id={`empty-state_background-fade-${isDarkTheme ? 'dark' : 'light'}_svg__a`}
x1="1947"
y1="1044.5"
x2="1947"
y2=".89"
gradientTransform="translate(0 1044.5) scale(1 -1)"
gradientUnits="userSpaceOnUse"
>
<stop
offset="0"
stopColor={stopColor1}
/>
<stop
offset="1"
stopColor={stopColor2}
/>
</linearGradient>
);
}

export default LinearGradientEmptyStateBackground;
7 changes: 7 additions & 0 deletions src/components/SVGDefinitionsProvider/index.native.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type ChildrenProps from '@src/types/utils/ChildrenProps';

function SVGDefinitionsProvider({children}: ChildrenProps) {
return children;
}

export default SVGDefinitionsProvider;
28 changes: 28 additions & 0 deletions src/components/SVGDefinitionsProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -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 <defs> cause rendering issues (like missing gradients).
*/
function SVGDefinitionsProvider({children}: ChildrenProps): ReactElement | null {
return (
<>
<svg
aria-hidden
style={{height: 0, width: 0, position: 'absolute'}}
>
<defs>
<LinearGradientEmptyStateBackground />
<LinearGradientEmptyStateBackground isDarkTheme />
</defs>
</svg>
{children}
</>
);
}

SVGDefinitionsProvider.displayName = 'SVGDefinitionsProvider';
export default SVGDefinitionsProvider;
25 changes: 19 additions & 6 deletions src/pages/home/report/AnimatedEmptyStateBackground.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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);
Expand Down Expand Up @@ -50,11 +53,21 @@ function AnimatedEmptyStateBackground() {

return (
<View style={StyleUtils.getReportWelcomeBackgroundContainerStyle()}>
<Animated.Image
source={illustrations.EmptyStateBackgroundImage}
<Animated.View
style={[StyleUtils.getReportWelcomeBackgroundImageStyle(shouldUseNarrowLayout), animatedStyles]}
resizeMode={windowWidth > maxBackgroundWidth ? 'repeat' : 'cover'}
/>
entering={FadeIn}
>
{Array.from({length: numberOfRepeats}).map((_, index) => (
<ImageSVG
// eslint-disable-next-line react/no-array-index-key
key={index}
src={illustrations.EmptyStateBackgroundImage}
width={numberOfRepeats > 1 ? illustrationWidth : undefined}
style={{position: 'absolute', left: index * illustrationWidth}}
preserveAspectRatio="xMidYMid slice"
/>
))}
</Animated.View>
</View>
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/styles/theme/illustrations/themes/dark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion src/styles/theme/illustrations/themes/light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
2 changes: 1 addition & 1 deletion src/styles/theme/illustrations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<SvgProps>;
ExampleCheckES: ImageSourcePropType;
ExampleCheckEN: ImageSourcePropType;
WorkspaceProfile: ImageSourcePropType;
Expand Down
2 changes: 1 addition & 1 deletion src/styles/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Loading