Skip to content

Commit

Permalink
improve
Browse files Browse the repository at this point in the history
  • Loading branch information
Christoph Pader committed Jan 7, 2024
1 parent 8715d7c commit 4ce49a3
Show file tree
Hide file tree
Showing 14 changed files with 144 additions and 244 deletions.
17 changes: 2 additions & 15 deletions src/components/Attachments/AttachmentCarousel/CarouselItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,6 @@ const propTypes = {
transactionID: PropTypes.string,
}).isRequired,

/** Whether there is only one element in the attachment carousel */
isSingleItem: PropTypes.bool.isRequired,

/** The index of the carousel item */
index: PropTypes.number.isRequired,

/** The index of the currently active carousel item */
activeIndex: PropTypes.number.isRequired,

/** onPress callback */
onPress: PropTypes.func,
};
Expand All @@ -54,7 +45,7 @@ const defaultProps = {
onPress: undefined,
};

function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) {
function CarouselItem({item, onPress}) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isAttachmentHidden} = useContext(ReportAttachmentsContext);
Expand Down Expand Up @@ -103,12 +94,8 @@ function CarouselItem({item, index, activeIndex, isSingleItem, onPress}) {
<AttachmentView
source={item.source}
file={item.file}
onPresS={onPress}
isAuthTokenRequired={item.isAuthTokenRequired}
isUsedInCarousel
isSingleCarouselItem={isSingleItem}
carouselItemIndex={index}
carouselActiveItemIndex={activeIndex}
onPress={onPress}
transactionID={item.transactionID}
/>
</View>
Expand Down
106 changes: 56 additions & 50 deletions src/components/Attachments/AttachmentCarousel/Pager/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import React, {useEffect, useImperativeHandle, useMemo, useRef, useState} from '
import {View} from 'react-native';
import {createNativeWrapper} from 'react-native-gesture-handler';
import PagerView from 'react-native-pager-view';
import Animated, {runOnJS, useAnimatedProps, useAnimatedReaction, useEvent, useHandler, useSharedValue} from 'react-native-reanimated';
import Animated, {runOnJS, useAnimatedReaction, useEvent, useHandler, useSharedValue} from 'react-native-reanimated';
import _ from 'underscore';
import CarouselItem from '@components/Attachments/AttachmentCarousel/CarouselItem';
import refPropTypes from '@components/refPropTypes';
import useThemeStyles from '@hooks/useThemeStyles';
import AttachmentCarouselPagerContext from './AttachmentCarouselPagerContext';
Expand Down Expand Up @@ -36,62 +37,71 @@ const pagerPropTypes = {
url: PropTypes.string,
}),
).isRequired,
renderItem: PropTypes.func.isRequired,
initialIndex: PropTypes.number,
activeSource: PropTypes.string.isRequired,
initialPage: PropTypes.number,
scrollEnabled: PropTypes.bool,
onPageSelected: PropTypes.func,
onTap: PropTypes.func,
onScaleChanged: PropTypes.func,
forwardedRef: refPropTypes,
};

const pagerDefaultProps = {
initialIndex: 0,
initialPage: 0,
scrollEnabled: true,
onPageSelected: () => {},
onTap: () => {},
onScaleChanged: () => {},
forwardedRef: null,
};

function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelected, onTap, onScaleChanged, forwardedRef}) {
function AttachmentCarouselPager({items, activeSource, initialPage, scrollEnabled, onPageSelected, onTap, onScaleChanged, forwardedRef}) {
const styles = useThemeStyles();
const shouldPagerScroll = useSharedValue(true);
const pagerRef = useRef(null);

const isSwipingInPager = useSharedValue(false);
const activeIndex = useSharedValue(initialIndex);
const activePage = useSharedValue(initialPage);
const [activePageState, setActivePageState] = useState(initialPage);

// Set active page initially and when initial page changes
useEffect(() => {
setActivePageState(initialPage);
activePage.value = initialPage;
}, [activePage, initialPage]);

const itemsMeta = useMemo(() => _.map(items, (item, index) => ({source: item.source, index, isActive: index === activePageState})), [activePageState, items]);

const isPagerSwiping = useSharedValue(false);
const pageScrollHandler = usePageScrollHandler(
{
onPageScroll: (e) => {
'worklet';

activeIndex.value = e.position;
isSwipingInPager.value = e.offset !== 0;
activePage.value = e.position;
isPagerSwiping.value = e.offset !== 0;
},
},
[],
);

const [activePage, setActivePage] = useState(initialIndex);

useEffect(() => {
setActivePage(initialIndex);
activeIndex.value = initialIndex;
}, [activeIndex, initialIndex]);

// we use reanimated for this since onPageSelected is called
// in the middle of the pager animation
const [isPagerSwipingState, setPagerSwipingState] = useState(false);
useAnimatedReaction(
() => isSwipingInPager.value,
(stillScrolling) => {
if (stillScrolling) {
return;
}

runOnJS(setActivePage)(activeIndex.value);
() => [isPagerSwiping.value],
(isSwiping) => {
runOnJS(setPagerSwipingState)(isSwiping);
},
);

const contextValue = useMemo(
() => ({
itemsMeta,
activePage: activePageState,
isPagerSwiping: isPagerSwipingState,
onTap,
onScaleChanged,
}),
[activePageState, isPagerSwipingState, itemsMeta, onScaleChanged, onTap],
);

useImperativeHandle(
forwardedRef,
() => ({
Expand All @@ -100,41 +110,37 @@ function AttachmentCarouselPager({items, renderItem, initialIndex, onPageSelecte
[],
);

const animatedProps = useAnimatedProps(() => ({
scrollEnabled: shouldPagerScroll.value,
}));

const contextValue = useMemo(
() => ({
onTap,
onScaleChanged,
pagerRef,
shouldPagerScroll,
isSwipingInPager,
}),
[isSwipingInPager, shouldPagerScroll, onScaleChanged, onTap],
const Content = useMemo(
() =>
_.map(items, (item, index) => (
<View
key={item.source}
style={styles.flex1}
>
<CarouselItem
item={item}
isSingleItem={items.length === 1}
index={index}
isFocused={index === activePageState && activeSource === item.source}
/>
</View>
)),
[activePageState, activeSource, items, styles.flex1],
);

return (
<AttachmentCarouselPagerContext.Provider value={contextValue}>
<AnimatedPagerView
pageMargin={40}
offscreenPageLimit={1}
scrollEnabled={scrollEnabled}
onPageScroll={pageScrollHandler}
animatedProps={animatedProps}
onPageSelected={onPageSelected}
ref={pagerRef}
style={styles.flex1}
initialPage={initialIndex}
initialPage={initialPage}
ref={pagerRef}
>
{_.map(items, (item, index) => (
<View
key={item.source}
style={styles.flex1}
>
{renderItem({item, index, isActive: index === activePage})}
</View>
))}
{Content}
</AnimatedPagerView>
</AttachmentCarouselPagerContext.Provider>
);
Expand Down
32 changes: 5 additions & 27 deletions src/components/Attachments/AttachmentCarousel/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import variables from '@styles/variables';
import ONYXKEYS from '@src/ONYXKEYS';
import {defaultProps, propTypes} from './attachmentCarouselPropTypes';
import CarouselButtons from './CarouselButtons';
import CarouselItem from './CarouselItem';
import extractAttachmentsFromReport from './extractAttachmentsFromReport';
import AttachmentCarouselPager from './Pager';
import useCarouselArrows from './useCarouselArrows';
Expand Down Expand Up @@ -88,25 +87,6 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,
[autoHideArrows, page, updatePage],
);

/**
* Defines how a single attachment should be rendered
* @param {{ reportActionID: String, isAuthTokenRequired: Boolean, source: String, file: { name: String }, hasBeenFlagged: Boolean }} item
* @returns {JSX.Element}
*/
const renderItem = useCallback(
({item, index, isActive}) => (
<CarouselItem
item={item}
isSingleItem={attachments.length === 1}
index={index}
activeIndex={page}
isFocused={isActive && activeSource === item.source}
onPress={() => setShouldShowArrows(!shouldShowArrows)}
/>
),
[activeSource, attachments.length, page, setShouldShowArrows, shouldShowArrows],
);

const handleScaleChange = useCallback(
(newScale) => {
const newIsZoomedOut = newScale === 1;
Expand All @@ -122,11 +102,7 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,
);

return (
<View
style={[styles.flex1, styles.attachmentCarouselContainer]}
onMouseEnter={() => setShouldShowArrows(true)}
onMouseLeave={() => setShouldShowArrows(false)}
>
<View style={[styles.flex1, styles.attachmentCarouselContainer]}>
{page == null ? (
<FullScreenLoadingIndicator />
) : (
Expand All @@ -152,8 +128,10 @@ function AttachmentCarousel({report, reportActions, parentReportActions, source,

<AttachmentCarouselPager
items={attachments}
renderItem={renderItem}
initialIndex={page}
initialPage={page}
scrollEnabled={isZoomedOut}
activeSource={activeSource}
onTap={() => setShouldShowArrows(!shouldShowArrows)}
onPageSelected={({nativeEvent: {position: newPage}}) => updatePage(newPage)}
onScaleChanged={handleScaleChange}
ref={pagerRef}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,7 @@ const propTypes = {
...withLocalizePropTypes,
};

function AttachmentViewImage({
source,
file,
isAuthTokenRequired,
isUsedInCarousel,
isSingleCarouselItem,
carouselItemIndex,
carouselActiveItemIndex,
isFocused,
loadComplete,
onPress,
onError,
isImage,
onScaleChanged,
translate,
}) {
function AttachmentViewImage({source, file, isAuthTokenRequired, isFocused, loadComplete, onPress, onError, isImage, onScaleChanged, translate}) {
const styles = useThemeStyles();
const children = (
<ImageView
Expand All @@ -37,10 +22,6 @@ function AttachmentViewImage({
fileName={file.name}
isAuthTokenRequired={isImage && isAuthTokenRequired}
isFocused={isFocused}
isUsedInCarousel={isUsedInCarousel}
isSingleCarouselItem={isSingleCarouselItem}
carouselItemIndex={carouselItemIndex}
carouselActiveItemIndex={carouselActiveItemIndex}
/>
);

Expand Down
8 changes: 0 additions & 8 deletions src/components/Attachments/AttachmentView/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@ function AttachmentView({
translate,
isFocused,
isUsedInCarousel,
isSingleCarouselItem,
carouselItemIndex,
carouselActiveItemIndex,
isUsedInAttachmentModal,
isWorkspaceAvatar,
fallbackSource,
Expand Down Expand Up @@ -138,8 +135,6 @@ function AttachmentView({
isFocused={isFocused}
isAuthTokenRequired={isAuthTokenRequired}
encryptedSourceUrl={encryptedSourceUrl}
carouselItemIndex={carouselItemIndex}
carouselActiveItemIndex={carouselActiveItemIndex}
onPress={onPress}
onScaleChanged={onScaleChanged}
onToggleKeyboard={onToggleKeyboard}
Expand Down Expand Up @@ -169,9 +164,6 @@ function AttachmentView({
loadComplete={loadComplete}
isFocused={isFocused}
isUsedInCarousel={isUsedInCarousel}
isSingleCarouselItem={isSingleCarouselItem}
carouselItemIndex={carouselItemIndex}
carouselActiveItemIndex={carouselActiveItemIndex}
isImage={isImage}
onPress={onPress}
onScaleChanged={onScaleChanged}
Expand Down
17 changes: 1 addition & 16 deletions src/components/Attachments/AttachmentView/propTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,6 @@ const attachmentViewPropTypes = {
/** Whether this AttachmentView is shown as part of a AttachmentCarousel */
isUsedInCarousel: PropTypes.bool,

/** When "isUsedInCarousel" is set to true, determines whether there is only one item in the carousel */
isSingleCarouselItem: PropTypes.bool,

/** Whether this AttachmentView is shown as part of an AttachmentModal */
isUsedInAttachmentModal: PropTypes.bool,

/** The index of the carousel item */
carouselItemIndex: PropTypes.number,

/** The index of the currently active carousel item */
carouselActiveItemIndex: PropTypes.number,

/** Function for handle on press */
onPress: PropTypes.func,

Expand All @@ -42,11 +30,8 @@ const attachmentViewDefaultProps = {
name: '',
},
isFocused: false,
isUsedInCarousel: false,
isSingleCarouselItem: false,
carouselItemIndex: 0,
carouselActiveItemIndex: 0,
isSingleElement: false,
isUsedInCarousel: false,
isUsedInAttachmentModal: false,
onPress: undefined,
onScaleChanged: () => {},
Expand Down
11 changes: 1 addition & 10 deletions src/components/ImageView/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ const propTypes = {
...imageViewPropTypes,
...zoomRangePropTypes,

/** Function for handle on press */
onPress: PropTypes.func,

/** Additional styles to add to the component */
style: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.object), PropTypes.object]),
};
Expand All @@ -26,20 +23,14 @@ const defaultProps = {
style: {},
};

function ImageView({isAuthTokenRequired, url, onScaleChanged, onPress, style, zoomRange, onError, isUsedInCarousel, isSingleCarouselItem, carouselItemIndex, carouselActiveItemIndex}) {
const hasSiblingCarouselItems = isUsedInCarousel && !isSingleCarouselItem;

function ImageView({isAuthTokenRequired, url, onScaleChanged, style, zoomRange, onError}) {
return (
<Lightbox
source={url}
zoomRange={zoomRange}
isAuthTokenRequired={isAuthTokenRequired}
onScaleChanged={onScaleChanged}
onPress={onPress}
onError={onError}
index={carouselItemIndex}
activeIndex={carouselActiveItemIndex}
hasSiblingCarouselItems={hasSiblingCarouselItems}
style={style}
/>
);
Expand Down
Loading

0 comments on commit 4ce49a3

Please sign in to comment.