-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Make Mobile Web and Native App Scanning Consistent #28411
Changes from 7 commits
b319def
32ff353
82082b1
9dd6164
0c33831
7c7cbe9
40d8604
63a02ce
08161c1
f58f98e
70d7069
d28849b
e3764d3
98519f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,72 @@ | ||
import React, {useEffect, useState} from 'react'; | ||
import {Camera} from 'react-native-vision-camera'; | ||
import {useNavigation} from '@react-navigation/native'; | ||
import React, {useEffect, useRef} from 'react'; | ||
import Webcam from 'react-webcam'; | ||
import {useIsFocused} 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, | ||
/* Flag to turn on/off the torch/flashlight - if available */ | ||
torchOn: PropTypes.bool, | ||
|
||
/* Forwarded ref */ | ||
forwardedRef: refPropTypes.isRequired, | ||
/* Callback function when media stream becomes available - user granted camera permissions and camera starts to work */ | ||
onUserMedia: PropTypes.func, | ||
|
||
/* Callback function passing torch/flashlight capability as bool param of the browser */ | ||
onTorchAvailability: PropTypes.func, | ||
}; | ||
|
||
const defaultProps = { | ||
onUserMedia: undefined, | ||
onTorchAvailability: undefined, | ||
torchOn: false, | ||
}; | ||
|
||
// Wraps a camera that will only be active when the tab is focused or as soon as it starts to become focused. | ||
function NavigationAwareCamera({cameraTabIndex, forwardedRef, ...props}) { | ||
// Get navigation to get initial isFocused value (only needed once during init!) | ||
const navigation = useNavigation(); | ||
const [isCameraActive, setIsCameraActive] = useState(navigation.isFocused()); | ||
|
||
// 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 | ||
function NavigationAwareCamera({torchOn, onTorchAvailability, ...props}, ref) { | ||
const trackRef = useRef(null); | ||
const isCameraActive = useIsFocused(); | ||
|
||
const handleOnUserMedia = (stream) => { | ||
if (props.onUserMedia) { | ||
props.onUserMedia(stream); | ||
} | ||
|
||
const [track] = stream.getVideoTracks(); | ||
const capabilities = track.getCapabilities(); | ||
if (capabilities.torch) { | ||
trackRef.current = track; | ||
} | ||
if (onTorchAvailability) { | ||
onTorchAvailability(!!capabilities.torch); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
const removeBlurListener = navigation.addListener('blur', () => { | ||
setIsCameraActive(false); | ||
}); | ||
const removeFocusListener = navigation.addListener('focus', () => { | ||
setIsCameraActive(true); | ||
}); | ||
if (!trackRef.current) { | ||
return; | ||
} | ||
|
||
return () => { | ||
removeBlurListener(); | ||
removeFocusListener(); | ||
}; | ||
}, [navigation]); | ||
trackRef.current.applyConstraints({ | ||
advanced: [{torch: torchOn}], | ||
}); | ||
}, [torchOn]); | ||
|
||
if (!isCameraActive) { | ||
return null; | ||
} | ||
return ( | ||
<Camera | ||
ref={forwardedRef} | ||
<Webcam | ||
audio={false} | ||
screenshotFormat="image/png" | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} | ||
isActive={isCameraActive} | ||
ref={ref} | ||
onUserMedia={handleOnUserMedia} | ||
/> | ||
); | ||
} | ||
|
||
NavigationAwareCamera.propTypes = propTypes; | ||
NavigationAwareCamera.displayName = 'NavigationAwareCamera'; | ||
NavigationAwareCamera.defaultProps = defaultProps; | ||
|
||
export default React.forwardRef((props, ref) => ( | ||
<NavigationAwareCamera | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} | ||
forwardedRef={ref} | ||
/> | ||
)); | ||
export default React.forwardRef(NavigationAwareCamera); |
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 NavigationAwareCamera({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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its -> It's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lukemorawski All threads have to be handled There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ |
||
// 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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its -> it's Also, the sentence structure in the comment isn't proper English, the comment could be passed through GPT or another tool with a prompt like "Improve language correctness" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ✅ |
||
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} | ||
/> | ||
); | ||
} | ||
|
||
NavigationAwareCamera.propTypes = propTypes; | ||
NavigationAwareCamera.displayName = 'NavigationAwareCamera'; | ||
|
||
export default React.forwardRef((props, ref) => ( | ||
<NavigationAwareCamera | ||
// eslint-disable-next-line react/jsx-props-no-spreading | ||
{...props} | ||
forwardedRef={ref} | ||
/> | ||
)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A downgrade?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lukemorawski Bump
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅