2021-02-19 08:23:54 -07:00
import React from 'react' ;
import { requireNativeComponent , NativeModules , ViewProps , NativeSyntheticEvent , findNodeHandle , NativeMethods , Platform } from 'react-native' ;
import type { CameraPhotoCodec , CameraVideoCodec } from './CameraCodec' ;
import type { ColorSpace , CameraDeviceFormat , CameraDevice } from './CameraDevice' ;
import type { ErrorWithCause } from './CameraError' ;
import { CameraCaptureError , CameraRuntimeError , tryParseNativeCameraError , isErrorWithCause } from './CameraError' ;
import type { CameraPreset } from './CameraPreset' ;
import type { CodeType , Code } from './Code' ;
import type { PhotoFile , TakePhotoOptions } from './PhotoFile' ;
import type { Point } from './Point' ;
import type { TakeSnapshotOptions } from './Snapshot' ;
import type { RecordVideoOptions , VideoFile } from './VideoFile' ;
2021-02-19 08:07:53 -07:00
//#region Types
2021-03-12 05:21:46 -07:00
export interface CameraProps extends ViewProps {
2021-02-19 08:07:53 -07:00
/ * *
2021-03-08 10:51:53 -07:00
* The Camera Device to use .
*
* See the [ Camera Devices ] ( https : //cuvent.github.io/react-native-vision-camera/docs/devices) section in the documentation for more information about Camera Devices.
*
* @example
2021-03-09 04:02:10 -07:00
* ` ` ` tsx
2021-03-08 10:51:53 -07:00
* const devices = useCameraDevices ( 'wide-angle-camera' )
* const device = devices . back
*
* return (
* < Camera
* device = { device }
* isActive = { true }
* style = { StyleSheet . absoluteFill }
* / >
* )
* ` ` `
2021-02-19 08:07:53 -07:00
* /
device : CameraDevice ;
/ * *
2021-03-08 10:51:53 -07:00
* Whether the Camera should actively stream video frames , or not . See the [ documentation about the ` isActive ` prop ] ( https : //cuvent.github.io/react-native-vision-camera/docs/devices#the-isactive-prop) for more information.
2021-03-03 04:37:43 -07:00
*
* This can be compared to a Video component , where ` isActive ` specifies whether the video is paused or not .
*
* > Note : If you fully unmount the ` <Camera> ` component instead of using ` isActive={false} ` , the Camera will take a bit longer to start again . In return , it will use less resources since the Camera will be completely destroyed when unmounted .
2021-02-19 08:07:53 -07:00
* /
isActive : boolean ;
2021-03-12 05:21:46 -07:00
//#region Common Props (torch, zoom)
2021-02-19 08:07:53 -07:00
/ * *
* Set the current torch mode .
*
* Note : The torch is only available on ` "back" ` cameras , and isn ' t supported by every phone .
*
* @default "off"
* /
2021-02-19 08:23:54 -07:00
torch ? : 'off' | 'on' ;
2021-02-19 08:07:53 -07:00
/ * *
* Specifies the zoom factor of the current camera , in percent . ( ` 0.0 ` - ` 1.0 ` )
*
2021-03-08 10:16:45 -07:00
* * * Note : * * Linearly increasing this value always appears logarithmic to the user .
*
2021-02-19 08:07:53 -07:00
* @default 0.0
* /
zoom? : number ;
/ * *
2021-03-08 10:51:53 -07:00
* Enables or disables the native pinch to zoom gesture .
*
* If you want to implement a custom zoom gesture , see [ the Zooming with Reanimated documentation ] ( https : //cuvent.github.io/react-native-vision-camera/docs/animated).
2021-02-19 08:07:53 -07:00
*
* @default false
* /
enableZoomGesture? : boolean ;
2021-03-12 05:21:46 -07:00
//#endregion
2021-02-19 08:07:53 -07:00
2021-03-12 05:21:46 -07:00
//#region Format/Preset selection
/ * *
* Automatically selects a camera format which best matches the given preset . Must be ` undefined ` when ` format ` is set !
* /
preset? : CameraPreset ;
/ * *
* Selects a given format . Must be ` undefined ` when ` preset ` is set !
* /
format? : CameraDeviceFormat ;
/ * *
* Specify the frames per second this camera should use . Make sure the given ` format ` includes a frame rate range with the given ` fps ` .
*
* Requires ` format ` to be set .
* /
fps? : number ;
/ * *
* Enables or disables HDR on this camera device . Make sure the given ` format ` supports HDR mode .
*
* Requires ` format ` to be set .
* /
hdr? : boolean ;
/ * *
* Enables or disables low - light boost on this camera device . Make sure the given ` format ` supports low - light boost .
*
* Requires ` format ` to be set .
* /
lowLightBoost? : boolean ;
/ * *
* Specifies the color space to use for this camera device . Make sure the given ` format ` contains the given ` colorSpace ` .
*
* Requires ` format ` to be set .
* /
colorSpace? : ColorSpace ;
//#endregion
/ * *
* Also captures data from depth - perception sensors . ( e . g . disparity maps )
*
* @default false
* /
enableDepthData? : boolean ;
/ * *
* A boolean specifying whether the photo render pipeline is prepared for portrait effects matte delivery .
*
* When enabling this , you must also set ` enableDepthData ` to ` true ` .
*
* @platform iOS 12.0 +
* @default false
* /
enablePortraitEffectsMatteDelivery? : boolean ;
/ * *
* Indicates whether the photo render pipeline should be configured to deliver high resolution still images
*
* @default false
* /
enableHighResolutionCapture? : boolean ;
//#region Events
2021-02-19 08:07:53 -07:00
/ * *
* Called when any kind of runtime error occured .
* /
onError ? : ( error : CameraRuntimeError ) = > void ;
/ * *
* Called when the camera was successfully initialized .
* /
onInitialized ? : ( ) = > void ;
2021-03-12 05:21:46 -07:00
// TODO: Remove once frameProcessors land
/ * *
* Specify the code types this camera can scan .
* /
scannableCodes? : CodeType [ ] ;
// TODO: Remove once frameProcessors land
/ * *
* Called when one or multiple codes have been scanned .
* /
onCodeScanned ? : ( codes : Code [ ] ) = > void ;
//#endregion
}
2021-02-19 08:07:53 -07:00
2021-02-19 08:23:54 -07:00
export type CameraPermissionStatus = 'authorized' | 'not-determined' | 'denied' | 'restricted' ;
export type CameraPermissionRequestResult = 'authorized' | 'denied' ;
2021-02-19 08:07:53 -07:00
interface OnErrorEvent {
code : string ;
message : string ;
cause? : ErrorWithCause ;
}
interface OnCodeScannedEvent {
codes : Code [ ] ;
}
2021-03-12 05:21:46 -07:00
type NativeCameraViewProps = Omit < CameraProps , ' device ' | ' onInitialized ' | ' onError ' | ' onCodeScanned ' > & {
cameraId : string ;
onInitialized ? : ( event : NativeSyntheticEvent < void > ) = > void ;
onError ? : ( event : NativeSyntheticEvent < OnErrorEvent > ) = > void ;
onCodeScanned ? : ( event : NativeSyntheticEvent < OnCodeScannedEvent > ) = > void ;
} ;
type RefType = React . Component < NativeCameraViewProps > & Readonly < NativeMethods > ;
2021-02-19 08:07:53 -07:00
//#endregion
// NativeModules automatically resolves 'CameraView' to 'CameraViewModule'
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const CameraModule = NativeModules . CameraView ;
2021-02-19 08:23:54 -07:00
if ( CameraModule == null ) console . error ( "Camera: Native Module 'CameraView' was null! Did you run pod install?" ) ;
2021-02-19 08:07:53 -07:00
interface CameraState {
/ * *
* The actual native ID for the camera device .
* /
cameraId? : string ;
}
/ * *
* # # # A powerful ` <Camera> ` component .
2021-03-03 04:37:43 -07:00
*
2021-03-08 10:51:53 -07:00
* Read the [ VisionCamera documentation ] ( https : //cuvent.github.io/react-native-vision-camera/) for more information.
*
2021-03-03 04:37:43 -07:00
* The ` <Camera> ` component ' s most important ( and therefore _required_ ) properties are :
*
2021-03-08 10:51:53 -07:00
* * { @linkcode CameraDeviceProps . device | device } : Specifies the { @linkcode CameraDevice } to use . Get a { @linkcode CameraDevice } by using the { @linkcode useCameraDevices | useCameraDevices ( ) } hook , or manually by using the { @linkcode Camera . getAvailableCameraDevices Camera . getAvailableCameraDevices ( ) } function .
* * { @linkcode CameraDynamicProps . isActive | isActive } : A boolean value that specifies whether the Camera should actively stream video frames or not . This can be compared to a Video component , where ` isActive ` specifies whether the video is paused or not . If you fully unmount the ` <Camera> ` component instead of using ` isActive={false} ` , the Camera will take a bit longer to start again .
2021-03-03 04:37:43 -07:00
*
* @example
2021-03-09 04:02:10 -07:00
* ` ` ` tsx
2021-03-03 04:37:43 -07:00
* function App() {
* const devices = useCameraDevices ( 'wide-angle-camera' )
* const device = devices . back
*
* if ( device == null ) return < LoadingView / >
* return (
* < Camera
* style = { StyleSheet . absoluteFill }
* device = { device }
* isActive = { true }
* / >
* )
* }
* ` ` `
*
* @component
2021-02-19 08:07:53 -07:00
* /
export class Camera extends React . PureComponent < CameraProps , CameraState > {
2021-03-07 06:23:14 -07:00
/ * *
* @internal
* /
2021-02-19 08:23:54 -07:00
static displayName = 'Camera' ;
2021-03-07 06:23:14 -07:00
/ * *
* @internal
* /
2021-02-19 08:07:53 -07:00
displayName = Camera . displayName ;
private readonly ref : React.RefObject < RefType > ;
2021-03-07 06:23:14 -07:00
/ * *
* @internal
* /
2021-02-19 08:07:53 -07:00
constructor ( props : CameraProps ) {
super ( props ) ;
this . state = { cameraId : undefined } ;
this . onInitialized = this . onInitialized . bind ( this ) ;
this . onError = this . onError . bind ( this ) ;
this . onCodeScanned = this . onCodeScanned . bind ( this ) ;
this . ref = React . createRef < RefType > ( ) ;
}
private get handle ( ) : number | null {
const nodeHandle = findNodeHandle ( this . ref . current ) ;
2021-02-19 08:23:54 -07:00
if ( nodeHandle == null ) console . error ( 'Camera: findNodeHandle(ref) returned null! Does the Camera view exist in the native view tree?' ) ;
2021-02-19 08:07:53 -07:00
return nodeHandle ;
}
//#region View-specific functions (UIViewManager)
/ * *
* Take a single photo and write it ' s content to a temporary file .
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraCaptureError } When any kind of error occured while capturing the photo . Use the { @linkcode CameraCaptureError . code | code } property to get the actual error
2021-03-08 10:30:23 -07:00
* @example
2021-03-09 04:02:10 -07:00
* ` ` ` ts
2021-03-08 10:30:23 -07:00
* const photo = await camera . current . takePhoto ( {
* qualityPrioritization : 'quality' ,
* flash : 'on' ,
* enableAutoRedEyeReduction : true
* } )
* ` ` `
2021-02-19 08:07:53 -07:00
* /
public async takePhoto ( options? : TakePhotoOptions ) : Promise < PhotoFile > {
try {
return await CameraModule . takePhoto ( this . handle , options ? ? { } ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Take a snapshot of the current preview view .
*
2021-03-08 10:51:53 -07:00
* This can be used as an alternative to { @linkcode Camera . takePhoto | takePhoto ( ) } if speed is more important than quality
*
* @throws { @linkcode CameraCaptureError } When any kind of error occured while taking a snapshot . Use the { @linkcode CameraCaptureError . code | code } property to get the actual error
2021-02-19 08:07:53 -07:00
*
* @platform Android
2021-03-08 10:30:23 -07:00
* @example
2021-03-09 04:02:10 -07:00
* ` ` ` ts
2021-03-08 10:30:23 -07:00
* const photo = await camera . current . takeSnapshot ( {
* quality : 85 ,
* skipMetadata : true
* } )
* ` ` `
2021-02-19 08:07:53 -07:00
* /
public async takeSnapshot ( options? : TakeSnapshotOptions ) : Promise < PhotoFile > {
2021-02-19 08:23:54 -07:00
if ( Platform . OS !== 'android' )
throw new CameraCaptureError ( 'capture/capture-type-not-supported' , ` 'takeSnapshot()' is not available on ${ Platform . OS } ! ` ) ;
2021-02-19 08:07:53 -07:00
try {
return await CameraModule . takeSnapshot ( this . handle , options ? ? { } ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Start a new video recording .
*
* Records in the following formats :
* * * * iOS * * : QuickTime ( ` .mov ` )
* * * * Android * * : MPEG4 ( ` .mp4 ` )
*
* @blocking This function is synchronized / blocking .
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraCaptureError } When any kind of error occured while starting the video recording . Use the { @linkcode CameraCaptureError . code | code } property to get the actual error
2021-02-19 08:07:53 -07:00
*
* @example
2021-03-09 04:02:10 -07:00
* ` ` ` ts
2021-02-19 08:07:53 -07:00
* camera . current . startRecording ( {
* onRecordingFinished : ( video ) = > console . log ( video ) ,
* onRecordingError : ( error ) = > console . error ( error ) ,
* } )
* setTimeout ( ( ) = > {
* camera . current . stopRecording ( )
* } , 5000 )
2021-03-03 04:37:43 -07:00
* ` ` `
2021-02-19 08:07:53 -07:00
* /
public startRecording ( options : RecordVideoOptions ) : void {
2021-02-19 08:23:54 -07:00
const { onRecordingError , onRecordingFinished , . . . passThroughOptions } = options ;
if ( typeof onRecordingError !== 'function' || typeof onRecordingFinished !== 'function' )
throw new CameraRuntimeError ( 'parameter/invalid-parameter' , 'The onRecordingError or onRecordingFinished functions were not set!' ) ;
const onRecordCallback = ( video? : VideoFile , error? : CameraCaptureError ) : void = > {
2021-02-19 08:07:53 -07:00
if ( error != null ) return onRecordingError ( error ) ;
if ( video != null ) return onRecordingFinished ( video ) ;
} ;
// TODO: Use TurboModules to either make this a sync invokation, or make it async.
try {
2021-02-19 08:23:54 -07:00
CameraModule . startRecording ( this . handle , passThroughOptions , onRecordCallback ) ;
2021-02-19 08:07:53 -07:00
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Stop the current video recording .
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraCaptureError } When any kind of error occured while stopping the video recording . Use the { @linkcode CameraCaptureError . code | code } property to get the actual error
2021-03-08 10:16:45 -07:00
*
2021-02-19 08:07:53 -07:00
* @example
2021-03-09 04:02:10 -07:00
* ` ` ` ts
2021-02-19 08:07:53 -07:00
* await camera . current . startRecording ( )
* setTimeout ( async ( ) = > {
* const video = await camera . current . stopRecording ( )
* } , 5000 )
2021-03-03 04:37:43 -07:00
* ` ` `
2021-02-19 08:07:53 -07:00
* /
public async stopRecording ( ) : Promise < void > {
try {
return await CameraModule . stopRecording ( this . handle ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Focus the camera to a specific point in the coordinate system .
2021-03-03 04:37:43 -07:00
* @param { Point } point The point to focus to . This should be relative to the Camera view ' s coordinate system ,
2021-02-19 08:07:53 -07:00
* and expressed in Pixel on iOS and Points on Android .
* * ` (0, 0) ` means * * top left * * .
* * ` (CameraView.width, CameraView.height) ` means * * bottom right * * .
*
* Make sure the value doesn 't exceed the CameraView' s dimensions .
2021-03-08 10:16:45 -07:00
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraRuntimeError } When any kind of error occured while focussing . Use the { @linkcode CameraRuntimeError . code | code } property to get the actual error
2021-03-08 10:30:23 -07:00
* @example
2021-03-09 04:02:10 -07:00
* ` ` ` ts
2021-03-08 10:30:23 -07:00
* await camera . current . focus ( {
* x : tapEvent.x ,
* y : tapEvent.y
* } )
* ` ` `
2021-02-19 08:07:53 -07:00
* /
public async focus ( point : Point ) : Promise < void > {
try {
return await CameraModule . focus ( this . handle , point ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Get a list of video codecs the current camera supports . Returned values are ordered by efficiency ( descending ) .
*
* This function can only be called after the camera has been initialized ,
2021-03-08 10:51:53 -07:00
* so only use this after the { @linkcode onInitialized | onInitialized ( ) } event has fired .
2021-03-08 10:16:45 -07:00
*
2021-03-08 10:51:53 -07:00
* @platform iOS
* @throws { @linkcode CameraRuntimeError } When any kind of error occured while getting available video codecs . Use the { @linkcode CameraRuntimeError . code | code } property to get the actual error
2021-02-19 08:07:53 -07:00
* /
public async getAvailableVideoCodecs ( ) : Promise < CameraVideoCodec [ ] > {
try {
return await CameraModule . getAvailableVideoCodecs ( this . handle ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Get a list of photo codecs the current camera supports . Returned values are ordered by efficiency ( descending ) .
*
* This function can only be called after the camera has been initialized ,
2021-03-08 10:51:53 -07:00
* so only use this after the { @linkcode onInitialized | onInitialized ( ) } event has fired .
2021-03-08 10:16:45 -07:00
*
2021-03-08 10:51:53 -07:00
* @platform iOS
* @throws { @linkcode CameraRuntimeError } When any kind of error occured while getting available photo codecs . Use the { @linkcode CameraRuntimeError . code | code } property to get the actual error
2021-02-19 08:07:53 -07:00
* /
public async getAvailablePhotoCodecs ( ) : Promise < CameraPhotoCodec [ ] > {
try {
return await CameraModule . getAvailablePhotoCodecs ( this . handle ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
//#endregion
//#region Static Functions (NativeModule)
/ * *
* Get a list of all available camera devices on the current phone .
2021-03-08 10:16:45 -07:00
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraRuntimeError } When any kind of error occured while getting all available camera devices . Use the { @linkcode CameraRuntimeError . code | code } property to get the actual error
2021-03-08 10:30:23 -07:00
* @example
2021-03-09 04:02:10 -07:00
* ` ` ` ts
2021-03-08 10:30:23 -07:00
* const devices = await Camera . getAvailableCameraDevices ( )
* const filtered = devices . filter ( ( d ) = > matchesMyExpectations ( d ) )
* const sorted = devices . sort ( sortDevicesByAmountOfCameras )
* return {
* back : sorted.find ( ( d ) = > d . position === "back" ) ,
* front : sorted.find ( ( d ) = > d . position === "front" )
* }
* ` ` `
2021-02-19 08:07:53 -07:00
* /
public static async getAvailableCameraDevices ( ) : Promise < CameraDevice [ ] > {
try {
return await CameraModule . getAvailableCameraDevices ( ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Gets the current Camera Permission Status . Check this before mounting the Camera to ensure
* the user has permitted the app to use the camera .
*
2021-03-08 10:51:53 -07:00
* To actually prompt the user for camera permission , use { @linkcode Camera . requestCameraPermission | requestCameraPermission ( ) } .
2021-03-08 10:16:45 -07:00
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraRuntimeError } When any kind of error occured while getting the current permission status . Use the { @linkcode CameraRuntimeError . code | code } property to get the actual error
2021-02-19 08:07:53 -07:00
* /
public static async getCameraPermissionStatus ( ) : Promise < CameraPermissionStatus > {
try {
return await CameraModule . getCameraPermissionStatus ( ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Gets the current Microphone - Recording Permission Status . Check this before mounting the Camera to ensure
* the user has permitted the app to use the microphone .
*
2021-03-08 10:51:53 -07:00
* To actually prompt the user for microphone permission , use { @linkcode Camera . requestMicrophonePermission | requestMicrophonePermission ( ) } .
2021-03-08 10:16:45 -07:00
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraRuntimeError } When any kind of error occured while getting the current permission status . Use the { @linkcode CameraRuntimeError . code | code } property to get the actual error
2021-02-19 08:07:53 -07:00
* /
public static async getMicrophonePermissionStatus ( ) : Promise < CameraPermissionStatus > {
try {
return await CameraModule . getMicrophonePermissionStatus ( ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Shows a "request permission" alert to the user , and resolves with the new camera permission status .
*
* If the user has previously blocked the app from using the camera , the alert will not be shown
* and ` "denied" ` will be returned .
2021-03-08 10:16:45 -07:00
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraRuntimeError } When any kind of error occured while requesting permission . Use the { @linkcode CameraRuntimeError . code | code } property to get the actual error
2021-02-19 08:07:53 -07:00
* /
public static async requestCameraPermission ( ) : Promise < CameraPermissionRequestResult > {
try {
return await CameraModule . requestCameraPermission ( ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
/ * *
* Shows a "request permission" alert to the user , and resolves with the new microphone permission status .
*
* If the user has previously blocked the app from using the microphone , the alert will not be shown
* and ` "denied" ` will be returned .
2021-03-08 10:16:45 -07:00
*
2021-03-08 10:51:53 -07:00
* @throws { @linkcode CameraRuntimeError } When any kind of error occured while requesting permission . Use the { @linkcode CameraRuntimeError . code | code } property to get the actual error
2021-02-19 08:07:53 -07:00
* /
public static async requestMicrophonePermission ( ) : Promise < CameraPermissionRequestResult > {
try {
return await CameraModule . requestMicrophonePermission ( ) ;
} catch ( e ) {
throw tryParseNativeCameraError ( e ) ;
}
}
//#endregion
//#region Events (Wrapped to maintain reference equality)
2021-03-12 05:21:46 -07:00
private onError ( event : NativeSyntheticEvent < OnErrorEvent > ) : void {
2021-02-19 08:23:54 -07:00
if ( event == null ) throw new Error ( 'onError() was invoked but event was null!' ) ;
2021-02-19 08:07:53 -07:00
if ( this . props . onError != null ) {
const error = event . nativeEvent ;
const cause = isErrorWithCause ( error . cause ) ? error.cause : undefined ;
this . props . onError (
// @ts-expect-error We're casting from unknown bridge types to TS unions, I expect it to hopefully work
2021-02-19 08:23:54 -07:00
new CameraRuntimeError ( error . code , error . message , cause ) ,
2021-02-19 08:07:53 -07:00
) ;
}
}
2021-02-19 08:23:54 -07:00
private onInitialized ( ) : void {
2021-02-19 08:07:53 -07:00
this . props . onInitialized ? . ( ) ;
}
2021-03-12 05:21:46 -07:00
private onCodeScanned ( event : NativeSyntheticEvent < OnCodeScannedEvent > ) : void {
2021-02-19 08:23:54 -07:00
if ( event == null ) throw new Error ( 'onCodeScanned() was invoked but event was null!' ) ;
if ( this . props . onCodeScanned == null )
console . warn ( 'Camera: onCodeScanned event was invoked but no listeners attached! Did you forget to remove the `scannableCodes` property?' ) ;
else this . props . onCodeScanned ( event . nativeEvent . codes ) ;
2021-02-19 08:07:53 -07:00
}
//#endregion
2021-03-07 06:23:14 -07:00
/ * *
* @internal
* /
2021-02-19 08:23:54 -07:00
static getDerivedStateFromProps ( props : CameraProps , state : CameraState ) : CameraState | null {
2021-02-19 08:07:53 -07:00
const newCameraId = props . device . id ;
2021-02-19 08:23:54 -07:00
if ( state . cameraId !== newCameraId ) return { . . . state , cameraId : newCameraId } ;
2021-02-19 08:07:53 -07:00
return null ;
}
2021-03-07 06:23:14 -07:00
/ * *
* @internal
* /
2021-02-19 08:07:53 -07:00
public render ( ) : React . ReactNode {
2021-03-12 05:21:46 -07:00
if ( this . state . cameraId == null ) throw new Error ( 'CameraId was null! Did you pass a valid `device`?' ) ;
2021-02-19 08:07:53 -07:00
// We remove the big `device` object from the props because we only need to pass `cameraId` to native.
const { device : _ , . . . props } = this . props ;
return (
< NativeCameraView
{ . . . props }
cameraId = { this . state . cameraId }
ref = { this . ref }
onInitialized = { this . onInitialized }
onError = { this . onError }
onCodeScanned = { this . onCodeScanned }
/ >
) ;
}
}
// requireNativeComponent automatically resolves 'CameraView' to 'CameraViewManager'
2021-03-12 05:21:46 -07:00
const NativeCameraView = requireNativeComponent < NativeCameraViewProps > (
2021-02-19 08:23:54 -07:00
'CameraView' ,
2021-02-19 08:07:53 -07:00
// @ts-expect-error because the type declarations are kinda wrong, no?
2021-02-19 08:23:54 -07:00
Camera ,
2021-02-19 08:07:53 -07:00
) ;