@b8safe/react-native-safe
Version:
Package for native integration of B8Safe SDK
317 lines (312 loc) • 10.6 kB
JavaScript
"use strict";
import { BlendMode, Canvas, createPicture, LinearGradient, Picture, Rect, Skia, vec } from '@shopify/react-native-skia';
import React, { useEffect, useRef, useState } from 'react';
import { Dimensions, Text, TouchableOpacity, View } from 'react-native';
import { useSharedValue as useWorkletSharedValue, Worklets } from 'react-native-worklets-core';
import { useB8SafeService } from "../index.js";
import { useSharedValue } from 'react-native-reanimated';
import { useCameraPermission, Camera, useFrameProcessor, runAtTargetFps, VisionCameraProxy, getCameraDevice } from 'react-native-vision-camera';
import PopIcon from "../illustrations/icons/PopIcon.js";
import ConnectionIcon from "../illustrations/icons/ConnectionIcon.js";
import styles from "./styles.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const plugin = VisionCameraProxy.initFrameProcessorPlugin('b8SafeProcessor', {});
const {
width,
height
} = Dimensions.get('screen');
const CameraScreen = ({
hash,
type,
title,
goBack,
onError,
children,
onMessage,
onSuccess,
onFinishCall,
titleStyle = {
fontSize: 15,
color: '#FFF'
},
showConnectionStatus = true,
messageTextStyle = {
textAlign: 'center',
fontSize: 19,
color: '#FFF'
}
}) => {
const {
pc,
stop,
start,
connected,
datachannel,
addDataChannelFunction,
removeDataChannelFunction,
changeStreamResolution
} = useB8SafeService() ?? {};
const layout = useRef({
width: 0,
height: 0,
x: 0,
y: 0
});
const {
hasPermission,
requestPermission
} = useCameraPermission();
let isMultiCamera = false;
const cameraRef = useRef(null);
const [message, setMessage] = useState(`Posicione ${type === 'front' ? 'a frente' : 'o verso'} do documento na moldura acima`);
const renderHud = canvas => {
const _width = layout.current.width;
const _height = layout.current.height;
const paint = Skia.Paint();
const color = Skia.Color(new Float32Array([0, 0, 0, 0.8]));
paint.setColor(color);
canvas.drawRect({
x: 0,
y: 0,
width: _width,
height: _height
}, paint);
const transparentPaint = Skia.Paint();
transparentPaint.setColor(Skia.Color(new Float32Array([0, 0, 0, 0.8])));
transparentPaint.setBlendMode(BlendMode.Clear);
const marginLeft = _width * 0.1;
const marginTop = _height * 0.22;
const w = _width * 0.8;
const h = w * 1.3082;
const _1 = {
x: 30,
y: 30
};
const _2 = {
x: 30,
y: 30
};
const _3 = {
x: 30,
y: 30
};
const _4 = {
x: 30,
y: 30
};
canvas.drawRRect({
topLeft: _1,
topRight: _2,
bottomRight: _3,
bottomLeft: _4,
rect: {
x: marginLeft,
y: marginTop,
width: w,
height: h
}
}, transparentPaint);
};
const picture = useSharedValue(createPicture(renderHud));
const handleLayout = event => {
if (children != null) return;
layout.current = event.nativeEvent.layout;
picture.value = createPicture(renderHud);
};
const checkPermission = () => {
if (!hasPermission) requestPermission();
setTimeout(() => {
// setCameraReady(width);
}, 500);
};
const finishCall = data => {
pc?.getSenders().forEach(sender => {
sender.replaceTrack(null);
});
if (onFinishCall != null) onFinishCall(data);
if (data.success) {
onSuccess != null && onSuccess(data);
} else {
stop?.();
onError != null && onError(data);
}
};
const upgradeResolution = data => {
// console.log('upgradeResolution', data);
changeStreamResolution?.(data);
};
const _onMessage = data => {
if (children != null) setMessage(data.message);
if (onMessage != null) onMessage(data);
};
const resolutionMultiplier = useWorkletSharedValue(0.1); //Usar 0.1 como default
const currentRequestPhoto = useWorkletSharedValue(false);
const lastFrameSent = useWorkletSharedValue(false);
const requestStillPhoto = data => {
currentRequestPhoto.value = data.requestId;
if (typeof data.resolutionMultiplier === 'string') data.resolutionMultiplier = parseFloat(data.resolutionMultiplier);
if (!isNaN(data.resolutionMultiplier)) {
// console.log('requestStillPhoto setting received resolutionMultiplier as '+data.resolutionMultiplier);
resolutionMultiplier.value = Number(data.resolutionMultiplier);
} else {
// console.log('requestStillPhoto setting default resolutionMultiplier as 0.1');
resolutionMultiplier.value = 0.1;
}
};
useEffect(() => {
if (hash !== '') start?.(hash, 'back');
addDataChannelFunction?.('onMessage', _onMessage);
addDataChannelFunction?.('finishCall', finishCall);
addDataChannelFunction?.('requestStillPhoto', requestStillPhoto);
addDataChannelFunction?.('upgradeResolution', upgradeResolution);
checkPermission();
return () => {
removeDataChannelFunction?.('onMessage');
removeDataChannelFunction?.('finishCall');
removeDataChannelFunction?.('requestStillPhoto');
removeDataChannelFunction?.('upgradeResolution');
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const videoHeight = width * 16 / 9;
const verticalSpace = (height - videoHeight) / 2;
const breakString = (str, size) => {
size = size ?? 60000;
const breakStr = [];
while (str.length > 0) {
breakStr.push(str.substring(0, size));
str = str.substring(size, str.length);
}
return breakStr;
};
const sendImageToServer = Worklets.createRunOnJS(base64Img => {
if (currentRequestPhoto.value === lastFrameSent.value) return;
// console.log('sendImageToServer:', base64Img.slice(0, 32), datachannel?.current?.readyState);
if (datachannel?.readyState === 'open') {
lastFrameSent.value = currentRequestPhoto.value;
// const b64Len = base64Img.length;
const parts = breakString(base64Img);
const frameId = currentRequestPhoto.value;
parts.forEach((item, index) => {
const str = `${frameId}|${index}|${item}`;
// if(index == 0) console.log(`${frameId}|${index}|${item}`);
//console.log(str.substring(0, 30));
datachannel.send(str);
});
const str = `${frameId}|-1|${parts.length}`;
// console.log(str);
datachannel.send(str);
}
});
let device;
const cameraDevices = Camera.getAvailableCameraDevices();
if (cameraDevices.length > 0) {
const firstCameraDevice = cameraDevices[0]; // Você pode escolher qualquer câmera da lista, porem o ideal é a da posição [0] pois ela é a camera principal do dispositivo
if (firstCameraDevice?.isMultiCam) {
isMultiCamera = true;
device = getCameraDevice(cameraDevices, 'back', {
physicalDevices: ['ultra-wide-angle-camera', 'wide-angle-camera', 'telephoto-camera']
});
} else {
device = getCameraDevice(cameraDevices, 'back');
}
} else {
console.warn('Nenhuma câmera disponível.');
}
const frameProcessor = useFrameProcessor(frame => {
'worklet';
runAtTargetFps(4, () => {
'worklet';
if (currentRequestPhoto.value === false || currentRequestPhoto.value === lastFrameSent.value) {
//Previne processamento se não estamos solicitando frames
// console.log('prevented frame processing - resolutionMultiplier: '+resolutionMultiplier);
return;
}
// console.log('frame will be processed - resolutionMultiplier: '+resolutionMultiplier);
const base64String = plugin?.call(frame, {
resolutionMultiplier: resolutionMultiplier.value
});
// console.log('base64String', base64String.substring(0, 64));
sendImageToServer(base64String);
});
}, [resolutionMultiplier, currentRequestPhoto, lastFrameSent, sendImageToServer]);
return /*#__PURE__*/_jsxs(View, {
style: styles.flex1,
children: [device != null && hasPermission ? /*#__PURE__*/_jsx(Camera, {
style: styles.cameraContainer,
ref: cameraRef,
frameProcessor: frameProcessor,
zoom: isMultiCamera === true ? 2 : 0,
isActive: true,
focusable: true,
device: device
}) : /*#__PURE__*/_jsx(View, {
style: styles.absoluteFill
}), /*#__PURE__*/_jsx(View, {
style: styles.absoluteFill,
onLayout: handleLayout,
children: children != null ? children : /*#__PURE__*/_jsxs(Canvas, {
style: styles.cameraMask,
children: [/*#__PURE__*/_jsx(Picture, {
picture: picture
}), /*#__PURE__*/_jsx(Rect, {
x: 0,
y: 0,
width: width,
height: verticalSpace + 70,
children: /*#__PURE__*/_jsx(LinearGradient, {
start: vec(0, verticalSpace),
end: vec(0, verticalSpace + 70),
colors: ['rgb(0,0,0)', 'rgba(0,0,0,0)']
})
}), /*#__PURE__*/_jsx(Rect, {
x: 0,
y: verticalSpace + videoHeight - 70,
width: width,
height: verticalSpace + 70,
children: /*#__PURE__*/_jsx(LinearGradient, {
start: vec(0, verticalSpace + videoHeight - 70),
end: vec(0, verticalSpace + videoHeight),
colors: ['rgba(0,0,0,0)', 'rgb(0,0,0)']
})
})]
})
}), children != null ? null : /*#__PURE__*/_jsxs(View, {
style: styles.cameraMaskContainer,
children: [/*#__PURE__*/_jsxs(View, {
style: styles.header,
children: [/*#__PURE__*/_jsx(View, {
style: styles.headerTitle,
children: /*#__PURE__*/_jsx(Text, {
style: titleStyle,
children: title ?? ''
})
}), /*#__PURE__*/_jsx(TouchableOpacity, {
style: styles.padding30,
onPress: goBack,
children: /*#__PURE__*/_jsx(PopIcon, {
width: 20,
height: 20,
strokeWidth: 16,
stroke: "#FFF"
})
})]
}), /*#__PURE__*/_jsx(View, {
style: styles.flex1
}), message !== '' ? /*#__PURE__*/_jsx(View, {
style: styles.footer,
children: /*#__PURE__*/_jsx(Text, {
style: messageTextStyle,
children: message
})
}) : null, showConnectionStatus ? /*#__PURE__*/_jsx(View, {
style: styles.connectionStatusContainer,
children: /*#__PURE__*/_jsx(ConnectionIcon, {
connected: connected
})
}) : null]
})]
});
};
export default CameraScreen;
//# sourceMappingURL=Camera.js.map