diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..abc45281 Binary files /dev/null and b/.DS_Store differ diff --git a/package.json b/package.json index 422f742f..79b7a8b5 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "dotenv": "^8.2.0", "expo": "^36.0.0", "expo-asset": "~8.0.0", - "expo-av": "^8.1.0", + "expo-av": "~8.0.0", "expo-camera": "~8.0.0", "expo-constants": "~8.0.0", "expo-font": "~8.0.0", diff --git a/src/.DS_Store b/src/.DS_Store index 7f1978ee..888c1f13 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/navigation/MainTabNavigator.tsx b/src/navigation/MainTabNavigator.tsx index 576e3097..e56b6bfa 100644 --- a/src/navigation/MainTabNavigator.tsx +++ b/src/navigation/MainTabNavigator.tsx @@ -10,6 +10,7 @@ import UploadingVideoScreen from '../screens/Main/EditStack/UploadingVideoScreen import RecordScreen from '../screens/Main/RecordStack/RecordScreen/RecordScreen' import SnippetSelectionScreen from '../screens/Main/EditStack/SnippetSelection/SnippetSelection' import Colors from '../constants/Colors' +import ViewScreen from '../screens/Main/RecordStack/ViewScreen/ViewScreen' // Tab #1 - Question Selection + Recording const RecordStack = createStackNavigator( @@ -19,7 +20,12 @@ const RecordStack = createStackNavigator( screen: RecordScreen, navigationOptions: { headerShown: false, - + } + }, + View: { + screen: ViewScreen, + navigationOptions: { + headerShown: false, } } }, @@ -29,7 +35,7 @@ RecordStack.navigationOptions = ({ navigation }) => { let tabBarVisible if (navigation.state.routes.length > 1) { navigation.state.routes.map(route => { - if (route.routeName === 'Record') { + if (route.routeName === 'Record' || route.routeName === 'View') { tabBarVisible = false } else { tabBarVisible = true diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js index c5621db2..3dea4f15 100644 --- a/src/redux/reducers/index.js +++ b/src/redux/reducers/index.js @@ -1,15 +1,66 @@ -const initialState = { - videos: [] -} - + +const initialState = + [{ + questionID: 1, + uri: null, + questionText: null, + }, + { + questionID: 2, + uri: null, + questionText: null, + }, + { + questionID: 3, + uri: null, + questionText: null, + }, + { + questionID: 4, + uri: null, + questionText: null, + },{ + questionID: 5, + uri: null, + questionText: null, + }, + { + questionID: 6, + uri: null, + questionText: null, + }, + { + questionID: 7, + uri: null, + questionText: null, + }, + { + questionID: 8, + uri: null, + questionText: null, + },{ + questionID: 9, + uri: null, + questionText: null, + }, + { + questionID: 10, + uri: null, + questionText: null, + }] + function rootReducer(state = initialState, action) { if (action.type === 'SAVE_VIDEO') { - return { - ...state, - videos: state.videos.concat(action.payload) - } + return state.map(videoObj => { + // found: update video + if (videoObj.questionID === action.payload.questionID) { + return action.payload + } + // not the video we are looking for continue + return videoObj + }) } return state } - + export default rootReducer \ No newline at end of file diff --git a/src/screens/Auth/LoginStack/LoginScreen/LoginScreen.tsx b/src/screens/Auth/LoginStack/LoginScreen/LoginScreen.tsx index 5b9d0ef7..84860ea9 100644 --- a/src/screens/Auth/LoginStack/LoginScreen/LoginScreen.tsx +++ b/src/screens/Auth/LoginStack/LoginScreen/LoginScreen.tsx @@ -17,7 +17,6 @@ interface Props { navigation: NavigationScreenProp, } - export default function LoginScreen(props: Props) { const {navigate} = props.navigation const [email, setEmail] = useState('') diff --git a/src/screens/Main/RecordStack/QuestionsScreen/QuestionsScreen.tsx b/src/screens/Main/RecordStack/QuestionsScreen/QuestionsScreen.tsx index a40f0574..4de6bc8a 100644 --- a/src/screens/Main/RecordStack/QuestionsScreen/QuestionsScreen.tsx +++ b/src/screens/Main/RecordStack/QuestionsScreen/QuestionsScreen.tsx @@ -4,7 +4,6 @@ import { Text, View, FlatList, - TouchableOpacity, TouchableHighlight, Alert, } from 'react-native' @@ -12,8 +11,6 @@ import styles from './styles' import { NavigationScreenProp, NavigationState } from 'react-navigation' import { BASE_PATH } from 'react-native-dotenv' import { connect } from 'react-redux' -import { saveVideo } from '../../../../redux/actions' -import { bindActionCreators } from 'redux' interface Question { ID: number, @@ -23,17 +20,17 @@ interface Question { interface Props { navigation: NavigationScreenProp, + videos: Array>, } /* AKA: Q&A screen */ -export default function QuestionsScreen(props: Props) { +function QuestionsScreen(props: Props) { const [selected, setSelected] = useState(null) const [questions, setQuestions] = useState>([]) const {push} = props.navigation const [modalVisibility, setModalVisibility] = useState(false) useEffect(() => { - fetch(`${BASE_PATH}/api/user/questions`) .then(res => res.json()) .then(data => { @@ -77,7 +74,11 @@ export default function QuestionsScreen(props: Props) { 'Edit your Answer clip', 'If you want to change your clip, do it here!', [ - {text: 'View Answer'}, + {text: 'View Answer', + onPress: () => { + push('View', {question: props.videos[item.ID-1].questionText, uri: props.videos[item.ID-1].uri}) + } + }, {text: 'Re-record Answer', onPress: () => { Alert.alert( @@ -87,7 +88,7 @@ export default function QuestionsScreen(props: Props) { {text: 'Re-record', onPress: () => { removeQuestion(item.ID) - push('Record', {question: item.text}) + push('Record', {question: item.text, questionID: item.ID}) } }, {text: 'Cancel', style: 'cancel'} @@ -98,7 +99,7 @@ export default function QuestionsScreen(props: Props) { ] ) } else { - push('Record', {question: item.text}) + push('Record', {question: item.text, questionID: item.ID}) } setSelected(item.ID) }} @@ -128,3 +129,11 @@ QuestionsScreen.navigationOptions = { ), headerStyle: {height: 140}, } + +const mapStateToProps = (state: any) => { + return { + videos: state + } +} + +export default connect(mapStateToProps)(QuestionsScreen) diff --git a/src/screens/Main/RecordStack/RecordScreen/RecordScreen.tsx b/src/screens/Main/RecordStack/RecordScreen/RecordScreen.tsx index 8db38f10..d9d59406 100644 --- a/src/screens/Main/RecordStack/RecordScreen/RecordScreen.tsx +++ b/src/screens/Main/RecordStack/RecordScreen/RecordScreen.tsx @@ -1,32 +1,62 @@ import React, { useState, useEffect } from 'react' import { Text, View, TouchableOpacity, Image, StatusBar} from 'react-native' import { Camera } from 'expo-camera' -import { Video } from 'expo-av' - import styles from './styles' import * as Permissions from 'expo-permissions' import * as MediaLibrary from 'expo-media-library' +import { Video } from 'expo-av' import { NavigationScreenProp, NavigationState, NavigationParams } from 'react-navigation' +import { connect } from 'react-redux' +import { saveVideo } from '../../../../redux/actions/index' +import { BASE_PATH } from 'react-native-dotenv' interface Props { + dispatch: Function, + saveVideo: Function, question: string, + questionID: number, navigation: NavigationScreenProp, } -export default function RecordScreen(props: Props) { - const {goBack} = props.navigation +function RecordScreen(props: Props) { + const {goBack, pop} = props.navigation const question = props.navigation.state.params.question + const questionID = props.navigation.state.params.questionID const [hasPermission, setHasPermission] = useState(false) const [camera, setCamera] = useState() const [isRecording, setIsRecording] = useState(false) const [cameraDirection, setCameraDirection] = useState(Camera.Constants.Type.front) const [video, setVideo] = useState(null) - async function saveVideo(){ + async function answerQuestion(){ + fetch(`${BASE_PATH}/api/user/questions`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + 'questionId': questionID, + }), + }) + .then(res => res.json()) + .then(data => { + console.log(data) + }) + .catch(error => { + console.log('Error: ' + error) + }) + } + + async function save(){ const asset = await MediaLibrary.createAssetAsync(video.uri) if (asset) { setVideo(null) } + answerQuestion() + const payload ={'questionID': questionID, 'uri': video.uri, 'questionText': question} + props.dispatch(saveVideo(payload)) + pop() } async function stopRecord(){ @@ -51,10 +81,10 @@ export default function RecordScreen(props: Props) { } useEffect(() => { - (async () => { - const { status } = await Permissions.getAsync(Permissions.CAMERA_ROLL, Permissions.CAMERA, Permissions.AUDIO_RECORDING) - setHasPermission(status === 'granted') + const { status: cameraPermission } = await Camera.requestPermissionsAsync() + const { status: cameraRollPermission } = await Permissions.askAsync(Permissions.CAMERA_ROLL) + setHasPermission(cameraPermission === 'granted' && cameraRollPermission === 'granted') })() }, []) @@ -64,73 +94,80 @@ export default function RecordScreen(props: Props) { if (hasPermission === false) { return No access to camera } - return ( - // { - // setCamera(ref) - // }} - // type={cameraDirection} - // > - // + ) + } +} + +export default connect()(RecordScreen) \ No newline at end of file diff --git a/src/screens/Main/RecordStack/RecordScreen/styles.tsx b/src/screens/Main/RecordStack/RecordScreen/styles.tsx index 59142103..2ca86b0e 100644 --- a/src/screens/Main/RecordStack/RecordScreen/styles.tsx +++ b/src/screens/Main/RecordStack/RecordScreen/styles.tsx @@ -98,14 +98,18 @@ const styles = StyleSheet.create({ bottomSection: { flex: 1, }, + videoBottom: { + flex: 1, + position: 'absolute', + }, saveButton: { backgroundColor: '#E5186E', transform: [ { rotateZ: '270deg'}], width: 79, height: 48, justifyContent: 'center', - left: '75%', - top: '15%', + left: '30%', + top: '1500%', borderRadius: 24, right: -Dimensions.get('window').width + (Dimensions.get('window').width * 0.25) }, @@ -114,7 +118,16 @@ const styles = StyleSheet.create({ color: '#fff', fontSize: 18, lineHeight: 21, - fontFamily: 'roboto-regular' + fontFamily: 'roboto-regular', + transform: [{ + rotate: '-180deg' + }], + }, + videoPlay: { + flex: 1, + transform: [{ + rotate: '-180deg' + }], } }) diff --git a/src/screens/Main/RecordStack/ViewScreen/ViewScreen.tsx b/src/screens/Main/RecordStack/ViewScreen/ViewScreen.tsx new file mode 100644 index 00000000..f1cc7d99 --- /dev/null +++ b/src/screens/Main/RecordStack/ViewScreen/ViewScreen.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import { Text, View, TouchableOpacity} from 'react-native' +import styles from './styles' +import { Video } from 'expo-av' +import { NavigationScreenProp, NavigationState, NavigationParams } from 'react-navigation' + +interface Props { + question: string, + uri: string, + navigation: NavigationScreenProp, +} + +export default function ViewScreen(props: Props) { + const {goBack} = props.navigation + const question = props.navigation.state.params.question + const uri = props.navigation.state.params.uri + + return( + + + ) +} \ No newline at end of file diff --git a/src/screens/Main/RecordStack/ViewScreen/styles.tsx b/src/screens/Main/RecordStack/ViewScreen/styles.tsx new file mode 100644 index 00000000..c71120eb --- /dev/null +++ b/src/screens/Main/RecordStack/ViewScreen/styles.tsx @@ -0,0 +1,77 @@ +import { StyleSheet, Dimensions } from 'react-native' + +const styles = StyleSheet.create({ + topSection: { + display: 'flex', + flex: 1, + flexDirection: 'row', + justifyContent: 'space-between', + paddingHorizontal: '7%', + alignItems: 'center', + transform: [{ + rotate: '180deg' + }] + }, + whiteButton: { + backgroundColor: '#fff', + width: 30, + height: 30, + borderRadius: 30, + alignSelf: 'center', + }, + whiteButtonOutline: { + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + width: 44, + height: 44, + top: Dimensions.get('window').width * 1.95, + borderRadius: 44, + borderWidth: 4, + borderColor: '#fff', + }, + middleSection: { + display: 'flex', + flexDirection: 'column', + flex: 4, + justifyContent: 'center', + alignItems: 'flex-start', + alignSelf: 'center', + transform: [{ + rotate: '-180deg' + }] + }, + overlay: { + position: 'absolute', + }, + question: { + backgroundColor: 'rgba(51,51,51,0.4)', + color: '#fff', + paddingVertical: 7, + paddingHorizontal: 10, + borderRadius: 5, + fontSize: 16, + textAlignVertical: 'center', + textAlign: 'center', + transform: [ { rotateZ: '270deg'}], + position: 'absolute', + right: -Dimensions.get('window').width + (Dimensions.get('window').width * 0.35), // funky but it works + top: Dimensions.get('window').width, + fontFamily: 'roboto-regular', + }, + bottomSection: { + flex: 1, + }, + videoBottom: { + flex: 1, + position: 'absolute', + }, + videoPlay: { + flex: 1, + transform: [{ + rotate: '-180deg' + }], + } +}) + +export default styles \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0b8b7d7c..73c11273 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4128,10 +4128,10 @@ expo-asset@~8.0.0: path-browserify "^1.0.0" url-parse "^1.4.4" -expo-av@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/expo-av/-/expo-av-8.1.0.tgz#376e2a821168cd83e997c6dd84350fab6b293f10" - integrity sha512-owWxOCyK+dPyT3pF4rDOf2W8Sn8LSEvfw1HvU9xM0wzH7xF0bw3Zs3wcXxvtfhnXAZ5dCDQfvZI5cXk+lml+IQ== +expo-av@~8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/expo-av/-/expo-av-8.0.0.tgz#fc892a0e4ff7e1eff630002e0652da3125d18113" + integrity sha512-n1qOXi0jk6stY4q5RZqmQS90Rg9HDDGWE7EfLABhcyUA1EGJJbrupjpn75sxzNYMksad+oiGp0NULTHlKfa01A== dependencies: lodash "^4.17.15" nullthrows "^1.1.0"