import { ApolloClient, useApolloClient } from "@apollo/client"; import { useIsFocused } from "@react-navigation/native"; import * as gql from "railbird-gql"; import React, { useCallback, useEffect, useRef, useState } from "react"; import { Button, StyleSheet, Text, View } from "react-native"; import * as RNFS from "react-native-fs"; import { Camera, CameraRuntimeError, Orientation, PhotoFile, VideoFile, useCameraDevice, useCameraFormat, useCameraPermission, } from "react-native-vision-camera"; import { RecordingButton } from "./capture-button"; import { useIsForeground } from "./is-foreground"; type Dictionary = { [key in KeyType]: ValueType; }; class StreamUploadManager { client: ApolloClient; videoId: number; nextUploadIdToRequest: number; highestUploadLinkObtained: number; prefetchedUploadLinks: Dictionary; constructor(client: ApolloClient, streamId: number) { this.client = client; this.videoId = streamId; this.highestUploadLinkObtained = -1; this.prefetchedUploadLinks = {}; } async uploadChunk({ filepath, index }: { filepath: string; index: number }) { const uploadUrl = await this.getUploadLink(index); const uploadRequest = RNFS.uploadFiles({ toUrl: uploadUrl, files: [{ filepath: filepath }], method: "PUT", headers: { "Content-Type": "application/octet-stream", }, begin: (res) => { console.log("Start upload", res); }, progress: (res) => { console.log("Uploading", res); }, }); console.log(JSON.stringify(uploadRequest)); const result = await uploadRequest.promise.catch((err) => console.log("Upload error!", err), ); if (response.statusCode == 200) { console.log(`${filepath} Uploaded`); } else { console.log("SERVER ERROR"); } } async getUploadLink(chunkId: number): Promise { return this.requestUploadLink(chunkId); } async requestUploadLink(chunkId: number): Promise { console.log(`Requesting ${chunkId}`); const result = await this.client.mutate({ mutation: gql.GetUploadLinkDocument, variables: { videoId: this.videoId, chunkIndex: chunkId }, }); console.log(JSON.stringify(result.data)); return result.data.getUploadLink.uploadUrl; } } export default function CameraScreen({ route, navigation, }): React.ReactElement { const apolloClient = useApolloClient(); const [createUpload, { data, loading, error }] = gql.useCreateUploadStreamMutation(); const [uploadManager, setUploadManager] = useState(null); useEffect(() => { if ( data && data.createUploadStream && data.createUploadStream.videoId && !uploadManager ) { const newVideoId = data.createUploadStream.videoId; console.log(`VideoId: ${newVideoId}`); setUploadManager(new StreamUploadManager(apolloClient, newVideoId)); } }, [data, uploadManager]); const camera = useRef(null); const { hasPermission, requestPermission } = useCameraPermission(); const [isCameraInitialized, setIsCameraInitialized] = useState(false); const isForeground = useIsForeground(); const isFocused = useIsFocused(); const isActive = isForeground && isFocused; const onError = useCallback((error: CameraRuntimeError) => { console.error(error); }, []); const onInitialized = useCallback(() => { console.log("Camera initialized!"); setIsCameraInitialized(true); createUpload({ variables: { videoName: "Test" } }); }, []); const onMediaCaptured = useCallback((media: PhotoFile | VideoFile) => { console.log(`Media captured! ${JSON.stringify(media)}`); }, []); const onVideoChunkReady = useCallback( (event) => { console.log( `Chunk ready in react-native ${JSON.stringify(event.nativeEvent)}`, ); uploadManager.uploadChunk(event.nativeEvent); }, [uploadManager], ); if (!hasPermission) { requestPermission(); // Error handling in case they refuse to give permission } const device = useCameraDevice("back"); const format = useCameraFormat(device, [ { videoResolution: { width: 3048, height: 2160 } }, { fps: 60 }, ]); // TODO(#60): setOrientation should be called when changes are detected const [orientation, setOrientation] = useState("portrait"); const toggleOrientation = () => { setOrientation((currentOrientation) => currentOrientation === "landscape-left" ? "portrait" : "landscape-left", ); }; // Replace with error handling if (device === null) { console.log(device); return ( Camera not available. Does user have permissions: {hasPermission} ); } return ( hasPermission && (