Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CP Stag] Only use tab animation when in tab navigator #27974

Merged
merged 3 commits into from
Sep 22, 2023
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
1 change: 1 addition & 0 deletions src/pages/EditRequestReceiptPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ function EditRequestReceiptPage({route, transactionID}) {
<ReceiptSelector
route={route}
transactionID={transactionID}
isInTabNavigator={false}
/>
</DragAndDropProvider>
</ScreenWrapper>
Expand Down
17 changes: 0 additions & 17 deletions src/pages/iou/ReceiptSelector/NavigationAwareCamera.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, {useEffect, useState} from 'react';
import {Camera} from 'react-native-vision-camera';
import {useTabAnimation} from '@react-navigation/material-top-tabs';
import {useNavigation} from '@react-navigation/native';
import PropTypes from 'prop-types';
import refPropTypes from '../../../components/refPropTypes';
Expand All @@ -19,22 +18,6 @@ function NavigationAwareCamera({cameraTabIndex, forwardedRef, ...props}) {
const navigation = useNavigation();
const [isCameraActive, setIsCameraActive] = useState(navigation.isFocused());

// Get the animation value from the tab navigator. Its a value between 0 and the
// number of pages we render in the tab navigator. When we even just slightly start to scroll to the camera page,
// (value is e.g. 0.001 on animation start) we want to activate the camera, so its as fast as possible active.
const tabPositionAnimation = useTabAnimation();

useEffect(() => {
const listenerId = tabPositionAnimation.addListener(({value}) => {
// Activate camera as soon the index is animating towards the `cameraTabIndex`
setIsCameraActive(value > cameraTabIndex - 1 && value < cameraTabIndex + 1);
});

return () => {
tabPositionAnimation.removeListener(listenerId);
};
}, [cameraTabIndex, tabPositionAnimation]);

// Note: The useEffect can be removed once VisionCamera V3 is used.
// Its only needed for android, because there is a native cameraX android bug. With out this flow would break the camera:
// 1. Open camera tab
Expand Down
77 changes: 77 additions & 0 deletions src/pages/iou/ReceiptSelector/TabNavigationAwareCamera.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import React, {useEffect, useState} from 'react';
import {Camera} from 'react-native-vision-camera';
import {useTabAnimation} from '@react-navigation/material-top-tabs';
import {useNavigation} from '@react-navigation/native';
import PropTypes from 'prop-types';
import refPropTypes from '../../../components/refPropTypes';

const propTypes = {
/* The index of the tab that contains this camera */
cameraTabIndex: PropTypes.number.isRequired,

/* Forwarded ref */
forwardedRef: refPropTypes.isRequired,
};

// Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused.
function TabNavigationAwareCamera({cameraTabIndex, forwardedRef, ...props}) {
// Get navigation to get initial isFocused value (only needed once during init!)
const navigation = useNavigation();
const [isCameraActive, setIsCameraActive] = useState(navigation.isFocused());

// Get the animation value from the tab navigator. Its a value between 0 and the
// number of pages we render in the tab navigator. When we even just slightly start to scroll to the camera page,
// (value is e.g. 0.001 on animation start) we want to activate the camera, so its as fast as possible active.
const tabPositionAnimation = useTabAnimation();

useEffect(() => {
const listenerId = tabPositionAnimation.addListener(({value}) => {
// Activate camera as soon the index is animating towards the `cameraTabIndex`
setIsCameraActive(value > cameraTabIndex - 1 && value < cameraTabIndex + 1);
});

return () => {
tabPositionAnimation.removeListener(listenerId);
};
}, [cameraTabIndex, tabPositionAnimation]);

// Note: The useEffect can be removed once VisionCamera V3 is used.
// Its only needed for android, because there is a native cameraX android bug. With out this flow would break the camera:
// 1. Open camera tab
// 2. Take a picture
// 3. Go back from the opened screen
// 4. The camera is not working anymore
useEffect(() => {
const removeBlurListener = navigation.addListener('blur', () => {
setIsCameraActive(false);
});
const removeFocusListener = navigation.addListener('focus', () => {
setIsCameraActive(true);
});

return () => {
removeBlurListener();
removeFocusListener();
};
}, [navigation]);

return (
<Camera
ref={forwardedRef}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
isActive={isCameraActive}
/>
);
}

TabNavigationAwareCamera.propTypes = propTypes;
TabNavigationAwareCamera.displayName = 'TabNavigationAwareCamera';

export default React.forwardRef((props, ref) => (
<TabNavigationAwareCamera
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
));
5 changes: 5 additions & 0 deletions src/pages/iou/ReceiptSelector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,17 @@ const propTypes = {

/** The id of the transaction we're editing */
transactionID: PropTypes.string,

/** Whether or not the receipt selector is in a tab navigator for tab animations */
// eslint-disable-next-line react/no-unused-prop-types
isInTabNavigator: PropTypes.bool,
};

const defaultProps = {
report: {},
iou: iouDefaultProps,
transactionID: '',
isInTabNavigator: true,
};

function ReceiptSelector(props) {
Expand Down
11 changes: 9 additions & 2 deletions src/pages/iou/ReceiptSelector/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {iouPropTypes, iouDefaultProps} from '../propTypes';
import NavigationAwareCamera from './NavigationAwareCamera';
import Navigation from '../../../libs/Navigation/Navigation';
import * as FileUtils from '../../../libs/fileDownload/FileUtils';
import TabNavigationAwareCamera from './TabNavigationAwareCamera';

const propTypes = {
/** React Navigation route */
Expand All @@ -47,12 +48,16 @@ const propTypes = {

/** The id of the transaction we're editing */
transactionID: PropTypes.string,

/** Whether or not the receipt selector is in a tab navigator for tab animations */
isInTabNavigator: PropTypes.bool,
};

const defaultProps = {
report: {},
iou: iouDefaultProps,
transactionID: '',
isInTabNavigator: true,
};

/**
Expand Down Expand Up @@ -80,7 +85,7 @@ function getImagePickerOptions(type) {
};
}

function ReceiptSelector({route, report, iou, transactionID}) {
function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator}) {
const devices = useCameraDevices('wide-angle-camera');
const device = devices.back;

Expand All @@ -96,6 +101,8 @@ function ReceiptSelector({route, report, iou, transactionID}) {

const {translate} = useLocalize();

const CameraComponent = isInTabNavigator ? TabNavigationAwareCamera : NavigationAwareCamera;

// We want to listen to if the app has come back from background and refresh the permissions status to show camera when permissions were granted
useEffect(() => {
const subscription = AppState.addEventListener('change', (nextAppState) => {
Expand Down Expand Up @@ -260,7 +267,7 @@ function ReceiptSelector({route, report, iou, transactionID}) {
</View>
)}
{permissions === RESULTS.GRANTED && device != null && (
<NavigationAwareCamera
<CameraComponent
ref={camera}
device={device}
style={[styles.cameraView]}
Expand Down
Loading