Created
August 19, 2021 16:18
-
-
Save fobos531/d69c7a859d2546765a0b65d41a3d528c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useRef, useState, useEffect } from 'react'; | |
import { View, Text, TouchableOpacity, StyleSheet, Pressable } from 'react-native'; | |
import { RNCamera } from 'react-native-camera'; | |
import { useSafeAreaInsets } from 'react-native-safe-area-context'; | |
import { StackNavigationProp } from '@react-navigation/stack'; | |
import { useIsFocused } from '@react-navigation/native'; | |
import LinearGradient from 'react-native-linear-gradient'; | |
import { useReactiveVar } from '@apollo/client'; | |
import ModalContainer from '@components/create-story/CreateModal'; | |
import { SuccessModal } from '@components/create-story'; | |
import { Icon } from '@components/common'; | |
import { ShutterIcon, SwithCameraIcon, ShutterSuccessIcon } from '@assets/icons/create-story/'; | |
import { CloseStoryIcon } from '@assets/icons/story-view'; | |
import { StopRecordingIcon } from '@assets/icons/index'; | |
import { StoryStackParamList } from '@navigation/stacks/StoryStack'; | |
import screen from '@navigation/screens'; | |
import colors from '@constants/colors'; | |
import { useStory, storyVar } from '@graphql/local/story'; | |
import dayjs from 'dayjs'; | |
type CreateNavigationProp = StackNavigationProp<StoryStackParamList, screen.CREATE>; | |
export interface CreateProps { | |
navigation: CreateNavigationProp; | |
} | |
const Create: React.FunctionComponent<CreateProps> = ({ navigation }) => { | |
const [activeCamera, setActiveCamera] = useState<'front' | 'back'>('back'); | |
const [isRecording, setIsRecording] = useState<boolean>(false); | |
const [longPressed, setLongPressed] = useState<boolean>(false); | |
const [duration, setDuration] = useState<number>(0); | |
const insets = useSafeAreaInsets(); | |
const camera = useRef<RNCamera>(null); | |
const isFocused = useIsFocused(); | |
const { addSlides, setCaptured } = useStory(storyVar); | |
const story = useReactiveVar(storyVar); | |
let timer: any; | |
const startRecording = () => { | |
// Mark that we started recording | |
setIsRecording(true); | |
// Actually start recording | |
// handleRecordVideo(); | |
const startTimer = () => { | |
timer = setInterval(() => { | |
// console.log('IN INTERVAL - time', dayjs().diff(timeStartedRecording, 'second').toString()); | |
setDuration((old) => old + 1); | |
}, 1000); | |
}; | |
console.log('TIMER REFERENCE', timer); | |
startTimer(); | |
}; | |
useEffect(() => { | |
console.log('DURATION', dayjs().startOf('day').second(duration).format('mm:ss')); | |
console.log('IS RECORDING', isRecording); | |
if (isRecording) { | |
} else { | |
} | |
}, [duration, isRecording]); | |
const handleSwitchCamera = () => { | |
if (activeCamera === 'back') { | |
setActiveCamera('front'); | |
} else { | |
setActiveCamera('back'); | |
} | |
}; | |
const handleTakePicture = async () => { | |
// Take picture and store it in the state | |
const data = await camera.current?.takePictureAsync({ pauseAfterCapture: true }); | |
if (!data) { | |
console.log('An error occured'); | |
return; | |
} | |
addSlides([ | |
{ | |
mediaType: 'image', | |
mediaUri: data.uri, | |
}, | |
]); | |
setCaptured(true); | |
}; | |
const handleRecordVideo = async () => { | |
if (camera.current) { | |
try { | |
const promise = camera.current?.recordAsync({ | |
quality: RNCamera.Constants.VideoQuality['1080p'], | |
}); | |
if (promise) { | |
const data = await promise; | |
console.log('VIDEO DATA', data); | |
// Example: VIDEO DATA {"deviceOrientation": 1, | |
//"isRecordingInterrupted": true, | |
//"uri": "file:///data/user/0/com.uxdotapp.restory/cache/Camera/baba8b91-fb52-4a1e-b4e9-afffc333dc51.mp4", | |
//"videoOrientation": 1} | |
// addSlides([ | |
// { | |
// mediaType: 'image', | |
// mediaUri: data.uri, | |
// }, | |
// ]); | |
} | |
} catch (err) { | |
console.log('ERROR OCCURED', err); | |
} | |
} | |
}; | |
return ( | |
<View style={[styles.flex, styles.blackBackground]}> | |
{story.captured && <SuccessModal />} | |
{/* {isFocused && <ModalContainer />} */} | |
{isFocused && <RNCamera ref={camera} type={activeCamera} style={styles.flex} />} | |
<View style={styles.overlayContainer}> | |
<View style={[styles.topOverlay, { marginTop: insets.top }]}> | |
<TouchableOpacity onPress={handleSwitchCamera}> | |
<SwithCameraIcon /> | |
</TouchableOpacity> | |
<View style={styles.recordingContainer}> | |
<View style={styles.recordingDot} /> | |
<Text style={styles.text}>{dayjs().startOf('day').second(duration).format('mm:ss')}</Text> | |
</View> | |
<TouchableOpacity onPress={navigation.goBack}> | |
<Icon icon={CloseStoryIcon} width={36} height={36} strokeWidth={2} /> | |
</TouchableOpacity> | |
</View> | |
<LinearGradient | |
colors={colors.darkGradient} | |
start={{ x: 0.5, y: 1 }} | |
end={{ x: 0.5, y: 0 }} | |
style={[styles.bottomOverlay, { paddingBottom: insets.bottom + 20 }]}> | |
<Pressable onPress={() => console.log('taken photo')}> | |
<Icon icon={story.captured ? ShutterSuccessIcon : ShutterIcon} color="none" width={70} height={70} /> | |
</Pressable> | |
<Pressable | |
onPress={() => { | |
if (longPressed) { | |
// Start recording | |
console.log('LONG PRESSED'); | |
startRecording(); | |
setLongPressed(false); | |
} else if (isRecording) { | |
setIsRecording(false); | |
clearInterval(timer); | |
} else { | |
// Take regular picture | |
handleTakePicture(); | |
} | |
}} | |
onPressIn={() => { | |
// Since onPress is always fired after onPressOut (which is always fired) | |
// we need a different way to differentiate between a long press and a regular press | |
// onPressIn will be fired only after a press of 500ms, so that's where we mark the | |
// longPressed variable as true | |
setLongPressed(true); | |
}} | |
onPressOut={() => { | |
// setIsRecording(false); | |
}} | |
unstable_pressDelay={500}> | |
<Icon icon={isRecording ? StopRecordingIcon : ShutterIcon} color="none" width={70} height={70} /> | |
</Pressable> | |
<TouchableOpacity style={styles.galleryContainer} onPress={() => navigation.push(screen.GALLERY)}> | |
<Text style={styles.text}>GALLERY</Text> | |
</TouchableOpacity> | |
</LinearGradient> | |
</View> | |
</View> | |
); | |
}; | |
const styles = StyleSheet.create({ | |
flex: { | |
flex: 1, | |
}, | |
blackBackground: { | |
backgroundColor: colors.black, | |
}, | |
overlayContainer: { | |
position: 'absolute', | |
height: '100%', | |
width: '100%', | |
justifyContent: 'space-between', | |
}, | |
topOverlay: { | |
width: '100%', | |
paddingTop: 25, | |
paddingHorizontal: 30, | |
flexDirection: 'row', | |
justifyContent: 'space-between', | |
alignItems: 'center', | |
}, | |
bottomOverlay: { | |
alignItems: 'center', | |
}, | |
galleryContainer: { | |
position: 'absolute', | |
left: 20, | |
bottom: 30, | |
}, | |
text: { | |
color: colors.white, | |
letterSpacing: 1, | |
}, | |
recordingContainer: { | |
flexDirection: 'row', | |
alignItems: 'center', | |
}, | |
recordingDot: { | |
width: 10, | |
height: 10, | |
borderRadius: 5, | |
backgroundColor: 'red', | |
marginRight: 5, | |
}, | |
}); | |
export default Create; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment