Skip to content

Commit e13adb6

Browse files
committed
fix 403 error, add loading state
1 parent 2420102 commit e13adb6

File tree

4 files changed

+79
-24
lines changed

4 files changed

+79
-24
lines changed

src/CONST/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,7 @@ const CONST = {
15361536
DEBOUNCE_HANDLE_SEARCH: 'debounce_handle_search',
15371537
FAST_SEARCH_TREE_CREATION: 'fast_search_tree_creation',
15381538
SHOW_HOVER_PREVIEW_DELAY: 270,
1539-
SHOW_HOVER_PREVIEW_ANIMATION_DURATION: 200,
1539+
SHOW_HOVER_PREVIEW_ANIMATION_DURATION: 250,
15401540
},
15411541
PRIORITY_MODE: {
15421542
GSD: 'gsd',

src/components/EReceiptWithSizeCalculation.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ type EReceiptWithSizeCalculationProps = {
1313
transactionItem?: TransactionListItemType | Transaction;
1414
};
1515

16+
const eReceiptAspectRatio = variables.eReceiptBGHWidth / variables.eReceiptBGHeight;
17+
1618
function EReceiptWithSizeCalculation(props: EReceiptWithSizeCalculationProps) {
1719
const [scaleFactor, setScaleFactor] = useState(0);
1820
const styles = useThemeStyles();
@@ -28,7 +30,11 @@ function EReceiptWithSizeCalculation(props: EReceiptWithSizeCalculationProps) {
2830
onLayout={onLayout}
2931
// We are applying transform of 0 translateZ to avoid a sub-pixel rendering error of a thin 1px line
3032
// appearing on EReceipts on web, specifically in chrome. More details in https://github.com/Expensify/App/pull/59944#issuecomment-2797249923.
31-
style={[styles.w100, styles.h100, {transform: `scale(${scaleFactor}) ${styles.translateZ0.transform as string}`, transformOrigin: 'top left'}]}
33+
style={[
34+
styles.w100,
35+
styles.h100,
36+
{transform: `scale(${scaleFactor}) ${styles.translateZ0.transform as string}`, transformOrigin: 'top left', aspectRatio: eReceiptAspectRatio},
37+
]}
3238
>
3339
<EReceipt
3440
// eslint-disable-next-line react/jsx-props-no-spreading

src/components/TransactionItemRow/DataCells/ReceiptCell.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ function ReceiptCell({transactionItem, isSelected, style}: {transactionItem: Tra
2323
const {hovered, bind} = useHover();
2424
const isEReceipt = transactionItem.hasEReceipt && !hasReceiptSource(transactionItem);
2525
let source = transactionItem?.receipt?.source ?? '';
26+
let previewSource = transactionItem?.receipt?.source ?? '';
27+
2628
if (source) {
2729
const filename = getFileName(source);
2830
const receiptURIs = getThumbnailAndImageURIs(transactionItem, null, filename);
2931
source = tryResolveUrlFromApiRoot(receiptURIs.thumbnail ?? receiptURIs.image ?? '');
32+
previewSource = tryResolveUrlFromApiRoot(receiptURIs.image ?? receiptURIs.thumbnail ?? '');
3033
}
3134

3235
return (
@@ -45,7 +48,7 @@ function ReceiptCell({transactionItem, isSelected, style}: {transactionItem: Tra
4548
source={source}
4649
isEReceipt={isEReceipt}
4750
transactionID={transactionItem.transactionID}
48-
shouldUseThumbnailImage={!transactionItem?.receipt?.source}
51+
shouldUseThumbnailImage
4952
isAuthTokenRequired
5053
fallbackIcon={Receipt}
5154
fallbackIconSize={20}
@@ -55,9 +58,10 @@ function ReceiptCell({transactionItem, isSelected, style}: {transactionItem: Tra
5558
loadingIconSize="small"
5659
loadingIndicatorStyles={styles.bgTransparent}
5760
transactionItem={transactionItem}
61+
shouldUseInitialObjectPosition
5862
/>
5963
<ReceiptPreview
60-
source={source}
64+
source={previewSource}
6165
hovered={hovered}
6266
isEReceipt={!!isEReceipt}
6367
transactionItem={transactionItem}

src/components/TransactionItemRow/ReceiptPreview/index.tsx

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
11
import React, {useCallback, useEffect, useRef, useState} from 'react';
22
import ReactDOM from 'react-dom';
33
import type {LayoutChangeEvent} from 'react-native';
4-
import {View} from 'react-native';
5-
import Animated, {FadeIn, FadeOut} from 'react-native-reanimated';
4+
import {ActivityIndicator, StyleSheet, View} from 'react-native';
5+
import Animated, {FadeIn, FadeOut, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
66
import DistanceEReceipt from '@components/DistanceEReceipt';
7-
import EReceipt from '@components/EReceipt';
8-
import BaseImage from '@components/Image/BaseImage';
7+
import EReceiptWithSizeCalculation from '@components/EReceiptWithSizeCalculation';
98
import type {ImageOnLoadEvent} from '@components/Image/types';
109
import useDebouncedState from '@hooks/useDebouncedState';
1110
import useResponsiveLayout from '@hooks/useResponsiveLayout';
11+
import useTheme from '@hooks/useTheme';
1212
import useThemeStyles from '@hooks/useThemeStyles';
1313
import useWindowDimensions from '@hooks/useWindowDimensions';
1414
import {isDistanceRequest} from '@libs/TransactionUtils';
1515
import variables from '@styles/variables';
16+
import Image from '@src/components/Image';
1617
import CONST from '@src/CONST';
1718
import type {Transaction} from '@src/types/onyx';
1819

19-
const eReceiptAspectRatio = variables.eReceiptBGHWidth / variables.eReceiptBGHeight;
20-
2120
type ReceiptPreviewProps = {
2221
/** Path to the image to be opened in the preview */
2322
source: string;
@@ -35,14 +34,20 @@ type ReceiptPreviewProps = {
3534
function ReceiptPreview({source, hovered, isEReceipt = false, transactionItem}: ReceiptPreviewProps) {
3635
const isDistanceEReceipt = isDistanceRequest(transactionItem);
3736
const styles = useThemeStyles();
37+
const theme = useTheme();
3838
const [eReceiptScaleFactor, setEReceiptScaleFactor] = useState(0);
3939
const [imageAspectRatio, setImageAspectRatio] = useState<string | number | undefined>(undefined);
4040
const [distanceEReceiptAspectRatio, setDistanceEReceiptAspectRatio] = useState<string | number | undefined>(undefined);
4141
const [shouldShow, debounceShouldShow, setShouldShow] = useDebouncedState(false, CONST.TIMING.SHOW_HOVER_PREVIEW_DELAY);
4242
const {shouldUseNarrowLayout} = useResponsiveLayout();
4343
const hasMeasured = useRef(false);
4444
const {windowHeight} = useWindowDimensions();
45+
const [isLoading, setIsLoading] = useState(true);
46+
const containerHeight = useSharedValue(150);
4547

48+
const animatedContainerStyle = useAnimatedStyle(() => ({
49+
height: containerHeight.get(),
50+
}));
4651
const handleDistanceEReceiptLayout = (e: LayoutChangeEvent) => {
4752
if (hasMeasured.current) {
4853
return;
@@ -57,8 +62,10 @@ function ReceiptPreview({source, hovered, isEReceipt = false, transactionItem}:
5762
}
5863
if (height * eReceiptScaleFactor > windowHeight - CONST.RECEIPT_PREVIEW_TOP_BOTTOM_MARGIN) {
5964
setDistanceEReceiptAspectRatio(variables.eReceiptBGHWidth / (windowHeight - CONST.RECEIPT_PREVIEW_TOP_BOTTOM_MARGIN));
65+
containerHeight.set(withTiming(windowHeight - CONST.RECEIPT_PREVIEW_TOP_BOTTOM_MARGIN, {duration: 300}));
6066
return;
6167
}
68+
containerHeight.set(withTiming(height, {duration: 300}));
6269
setDistanceEReceiptAspectRatio(variables.eReceiptBGHWidth / height);
6370
setEReceiptScaleFactor(width / variables.eReceiptBGHWidth);
6471
};
@@ -75,17 +82,21 @@ function ReceiptPreview({source, hovered, isEReceipt = false, transactionItem}:
7582
);
7683

7784
const handleLoad = useCallback(
78-
(event: ImageOnLoadEvent) => {
79-
const {width, height} = event.nativeEvent;
80-
85+
(e: ImageOnLoadEvent) => {
86+
const {width, height} = e.nativeEvent;
8187
updateImageAspectRatio(width, height);
88+
setIsLoading(false);
89+
90+
if (width && height) {
91+
const scaledHeight = (height / width) * 380;
92+
containerHeight.set(withTiming(scaledHeight, {duration: 300}));
93+
}
8294
},
83-
[updateImageAspectRatio],
95+
[updateImageAspectRatio, containerHeight],
8496
);
8597

86-
const handleEReceiptLayout = (e: LayoutChangeEvent) => {
87-
const {width} = e.nativeEvent.layout;
88-
setEReceiptScaleFactor(width / variables.eReceiptBGHWidth);
98+
const handleError = () => {
99+
setIsLoading(false);
89100
};
90101

91102
useEffect(() => {
@@ -103,22 +114,55 @@ function ReceiptPreview({source, hovered, isEReceipt = false, transactionItem}:
103114
<Animated.View
104115
entering={FadeIn.duration(CONST.TIMING.SHOW_HOVER_PREVIEW_ANIMATION_DURATION)}
105116
exiting={FadeOut.duration(CONST.TIMING.SHOW_HOVER_PREVIEW_ANIMATION_DURATION)}
106-
style={[styles.receiptPreview, styles.flexColumn, styles.alignItemsCenter, styles.justifyContentStart]}
117+
style={[
118+
styles.receiptPreview,
119+
styles.flexColumn,
120+
styles.alignItemsCenter,
121+
styles.justifyContentStart,
122+
animatedContainerStyle, // smoothly animated height
123+
]}
107124
>
108125
{shouldShowImage ? (
109126
<View style={[styles.w100]}>
110-
<BaseImage
127+
{isLoading && (
128+
<View style={[StyleSheet.absoluteFillObject, {top: 75}]}>
129+
<ActivityIndicator
130+
color={theme.spinner}
131+
size="large"
132+
/>
133+
</View>
134+
)}
135+
136+
<Image
111137
source={{uri: source}}
112-
style={[styles.w100, {aspectRatio: imageAspectRatio}]}
138+
style={[
139+
styles.w100,
140+
{aspectRatio: imageAspectRatio ?? 1},
141+
isLoading && {opacity: 0}, // hide until loaded
142+
]}
143+
onLoadStart={() => {
144+
if (isLoading) {
145+
return;
146+
}
147+
setIsLoading(true);
148+
}}
149+
onError={handleError}
113150
onLoad={handleLoad}
151+
isAuthTokenRequired
114152
/>
115153
</View>
116154
) : (
117155
<View style={styles.receiptPreviewEReceiptsContainer}>
118156
{shouldShowDistanceEReceipt ? (
119157
<View
120158
onLayout={handleDistanceEReceiptLayout}
121-
style={[{transformOrigin: 'center', scale: eReceiptScaleFactor, aspectRatio: distanceEReceiptAspectRatio}]}
159+
style={[
160+
{
161+
transformOrigin: 'center',
162+
scale: eReceiptScaleFactor,
163+
aspectRatio: distanceEReceiptAspectRatio,
164+
},
165+
]}
122166
>
123167
<DistanceEReceipt
124168
transaction={transactionItem}
@@ -127,10 +171,11 @@ function ReceiptPreview({source, hovered, isEReceipt = false, transactionItem}:
127171
</View>
128172
) : (
129173
<View
130-
onLayout={handleEReceiptLayout}
131-
style={[styles.receiptPreviewEReceipt, {aspectRatio: eReceiptAspectRatio, scale: eReceiptScaleFactor}]}
174+
onLayout={() => {
175+
containerHeight.set(withTiming(variables.eReceiptBGHeight, {duration: 300}));
176+
}}
132177
>
133-
<EReceipt
178+
<EReceiptWithSizeCalculation
134179
transactionID={transactionItem.transactionID}
135180
transactionItem={transactionItem}
136181
/>

0 commit comments

Comments
 (0)