Merge pull request 'Finalize recording' (#117) from loewy/finalize-video-flow into master
Reviewed-on: railbird/railbird-mobile#117 Reviewed-by: Kat Huang <kkathuang@gmail.com>
This commit is contained in:
commit
7bddeca783
@ -31,7 +31,7 @@ module.exports = {
|
|||||||
// Best Practices
|
// Best Practices
|
||||||
eqeqeq: ["error", "always"], // Enforce '===' instead of '=='
|
eqeqeq: ["error", "always"], // Enforce '===' instead of '=='
|
||||||
curly: ["error", "multi-line", "consistent"], // Require curly braces for all control statements
|
curly: ["error", "multi-line", "consistent"], // Require curly braces for all control statements
|
||||||
"no-unused-vars": "warn", // Warn about variables that are declared but not used
|
"no-unused-vars": "off", // Not needed with TypeScript
|
||||||
|
|
||||||
// React Specific
|
// React Specific
|
||||||
"react/jsx-filename-extension": [1, { extensions: [".tsx"] }], // Allow jsx syntax in .tsx files
|
"react/jsx-filename-extension": [1, { extensions: [".tsx"] }], // Allow jsx syntax in .tsx files
|
||||||
|
2
app.json
2
app.json
@ -38,7 +38,7 @@
|
|||||||
"googleServicesFile": "./google-services.json"
|
"googleServicesFile": "./google-services.json"
|
||||||
},
|
},
|
||||||
"web": {
|
"web": {
|
||||||
"favicon": "./src/assets/favicon.png"
|
"favicon": "./src/assets/icons/favicon.png"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -4,7 +4,6 @@ import * as gql from "railbird-gql";
|
|||||||
import React, { useCallback, useEffect, useRef, useState } from "react";
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { Button, StyleSheet, Text, View } from "react-native";
|
import { Button, StyleSheet, Text, View } from "react-native";
|
||||||
import * as RNFS from "react-native-fs";
|
import * as RNFS from "react-native-fs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Camera,
|
Camera,
|
||||||
CameraRuntimeError,
|
CameraRuntimeError,
|
||||||
@ -19,6 +18,7 @@ import { RecordingButton } from "./capture-button";
|
|||||||
import { useIsForeground } from "./is-foreground";
|
import { useIsForeground } from "./is-foreground";
|
||||||
|
|
||||||
type Dictionary<KeyType extends string | number | symbol, ValueType> = {
|
type Dictionary<KeyType extends string | number | symbol, ValueType> = {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
[key in KeyType]: ValueType;
|
[key in KeyType]: ValueType;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,6 +111,7 @@ export default function CameraScreen({
|
|||||||
navigation,
|
navigation,
|
||||||
}): React.ReactElement {
|
}): React.ReactElement {
|
||||||
const apolloClient = useApolloClient();
|
const apolloClient = useApolloClient();
|
||||||
|
const { params } = route;
|
||||||
const [createUpload, { data, loading, error }] =
|
const [createUpload, { data, loading, error }] =
|
||||||
gql.useCreateUploadStreamMutation();
|
gql.useCreateUploadStreamMutation();
|
||||||
|
|
||||||
@ -127,6 +128,7 @@ export default function CameraScreen({
|
|||||||
console.log(`VideoId: ${newVideoId}`);
|
console.log(`VideoId: ${newVideoId}`);
|
||||||
setUploadManager(new StreamUploadManager(apolloClient, newVideoId));
|
setUploadManager(new StreamUploadManager(apolloClient, newVideoId));
|
||||||
}
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [data, uploadManager]);
|
}, [data, uploadManager]);
|
||||||
|
|
||||||
const camera = useRef<Camera>(null);
|
const camera = useRef<Camera>(null);
|
||||||
@ -143,14 +145,24 @@ export default function CameraScreen({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onInitialized = useCallback(() => {
|
const onInitialized = useCallback(() => {
|
||||||
console.log("Camera initialized!");
|
|
||||||
setIsCameraInitialized(true);
|
setIsCameraInitialized(true);
|
||||||
createUpload({ variables: { videoName: "Test" } });
|
createUpload({
|
||||||
|
variables: { videoName: params?.sessionName ?? "New session" },
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onMediaCaptured = useCallback((media: PhotoFile | VideoFile) => {
|
const onMediaCaptured = useCallback(
|
||||||
console.log(`Media captured! ${JSON.stringify(media)}`);
|
(media: PhotoFile | VideoFile) => {
|
||||||
}, []);
|
console.log(`Media captured! ${JSON.stringify(media)}`);
|
||||||
|
navigation.push("SaveVideo", {
|
||||||
|
videoId: uploadManager?.videoId,
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[uploadManager, navigation, params],
|
||||||
|
);
|
||||||
|
|
||||||
const onVideoChunkReady = useCallback(
|
const onVideoChunkReady = useCallback(
|
||||||
(event) => {
|
(event) => {
|
||||||
@ -179,7 +191,6 @@ export default function CameraScreen({
|
|||||||
|
|
||||||
// Replace with error handling
|
// Replace with error handling
|
||||||
if (device === null) {
|
if (device === null) {
|
||||||
console.log(device);
|
|
||||||
// hasPermission redundant here - user should not be able to launch camera without permissions
|
// hasPermission redundant here - user should not be able to launch camera without permissions
|
||||||
return (
|
return (
|
||||||
<Text>
|
<Text>
|
||||||
|
154
src/component/video/use-video-details.tsx
Normal file
154
src/component/video/use-video-details.tsx
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import * as gql from "railbird-gql";
|
||||||
|
import { useCallback, useState } from "react";
|
||||||
|
// @ts-ignore
|
||||||
|
import { useCameraPermission } from "react-native-vision-camera";
|
||||||
|
import { showAlert } from "../../lib/alert-messages";
|
||||||
|
|
||||||
|
// TODO: #122 should decide on what values/labels go here.
|
||||||
|
enum GameType {
|
||||||
|
FreePlay = "freePlay",
|
||||||
|
StraightPool = "straightPool",
|
||||||
|
NineBall = "nineBall",
|
||||||
|
}
|
||||||
|
// TODO: #122 should be int -- inch value of table size.
|
||||||
|
enum TableSize {
|
||||||
|
NineFoot = "nineFoot",
|
||||||
|
EightFoot = "eightFoot",
|
||||||
|
SevenFoot = "sevenFoot",
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VideoUserInputMeta {
|
||||||
|
sessionName: string;
|
||||||
|
gameType: GameType | null;
|
||||||
|
tableSize: TableSize | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VideoFlowInputParams extends VideoUserInputMeta {
|
||||||
|
videoId?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useDropdown<T>(
|
||||||
|
initialValue: T | null,
|
||||||
|
options: Array<{ label: string; value: T }>,
|
||||||
|
closeOthers: () => void,
|
||||||
|
) {
|
||||||
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
|
const [value, setValue] = useState<T | null>(initialValue);
|
||||||
|
const [optionsList, setOptionsList] = useState(options);
|
||||||
|
|
||||||
|
const toggleOpen = useCallback(() => {
|
||||||
|
if (!isDropdownOpen) {
|
||||||
|
closeOthers();
|
||||||
|
}
|
||||||
|
setIsDropdownOpen(!isDropdownOpen);
|
||||||
|
}, [isDropdownOpen, closeOthers]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isDropdownOpen,
|
||||||
|
toggleOpen,
|
||||||
|
value,
|
||||||
|
setValue,
|
||||||
|
optionsList,
|
||||||
|
setOptionsList,
|
||||||
|
setIsDropdownOpen,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useVideoDetails = ({
|
||||||
|
params: {
|
||||||
|
mode,
|
||||||
|
videoId,
|
||||||
|
sessionName: initialSessionName,
|
||||||
|
gameType: initialGameType,
|
||||||
|
tableSize: initialTableSize,
|
||||||
|
},
|
||||||
|
navigation,
|
||||||
|
}) => {
|
||||||
|
const initialState = {
|
||||||
|
sessionName: initialSessionName || "",
|
||||||
|
gameType: initialGameType || null,
|
||||||
|
tableSize: initialTableSize || null,
|
||||||
|
};
|
||||||
|
const { hasPermission, requestPermission } = useCameraPermission();
|
||||||
|
|
||||||
|
const [sessionName, setSessionName] = useState<string>(
|
||||||
|
initialState.sessionName,
|
||||||
|
);
|
||||||
|
|
||||||
|
const closeAllDropdowns = useCallback(() => {
|
||||||
|
gameType.setIsDropdownOpen(false);
|
||||||
|
tableSize.setIsDropdownOpen(false);
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const gameType = useDropdown<GameType | null>(
|
||||||
|
initialState.gameType,
|
||||||
|
[
|
||||||
|
{ label: "Free Play", value: GameType.FreePlay },
|
||||||
|
{ label: "Straight Pool", value: GameType.StraightPool },
|
||||||
|
{ label: "Nine Ball", value: GameType.NineBall },
|
||||||
|
],
|
||||||
|
closeAllDropdowns,
|
||||||
|
);
|
||||||
|
|
||||||
|
const tableSize = useDropdown<TableSize | null>(
|
||||||
|
initialState.tableSize,
|
||||||
|
[
|
||||||
|
{ label: `9'`, value: TableSize.NineFoot },
|
||||||
|
{ label: `8'`, value: TableSize.EightFoot },
|
||||||
|
{ label: `7'`, value: TableSize.SevenFoot },
|
||||||
|
],
|
||||||
|
closeAllDropdowns,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [terminateUploadStream, { loading, error }] =
|
||||||
|
gql.useTerminateUploadStreamMutation();
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
// Check permissions
|
||||||
|
if (!hasPermission) {
|
||||||
|
try {
|
||||||
|
const permissionResult = await requestPermission();
|
||||||
|
if (permissionResult.status !== "granted") {
|
||||||
|
return showAlert("camera");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return showAlert("permissionError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate if starting flow, terminateUploadStream if completing flow
|
||||||
|
if (mode === "start-video") {
|
||||||
|
const params: VideoFlowInputParams = {
|
||||||
|
sessionName,
|
||||||
|
gameType: gameType.value,
|
||||||
|
tableSize: tableSize.value,
|
||||||
|
};
|
||||||
|
navigation.push("Camera", params);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await terminateUploadStream({
|
||||||
|
variables: {
|
||||||
|
videoId,
|
||||||
|
videoName: sessionName,
|
||||||
|
tableSize: tableSize.value,
|
||||||
|
gameType: gameType.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
navigation.push("Tabs");
|
||||||
|
} catch (err) {
|
||||||
|
showAlert("terminateUpload");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
sessionName,
|
||||||
|
setSessionName,
|
||||||
|
gameType,
|
||||||
|
tableSize,
|
||||||
|
handleSubmit,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
};
|
@ -50,7 +50,6 @@ export const ClientProvider: React.FC<Props> = ({ children }) => {
|
|||||||
|
|
||||||
const authMiddleware = new ApolloLink((operation, forward) => {
|
const authMiddleware = new ApolloLink((operation, forward) => {
|
||||||
const { key, value } = authHeader;
|
const { key, value } = authHeader;
|
||||||
console.log("Auth Key", key, "Value", value);
|
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
operation.setContext({
|
operation.setContext({
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -8,6 +8,10 @@ interface PermissionMessage {
|
|||||||
message: string;
|
message: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
interface ApiError {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const CAMERA_PERMISSION_DENIED: PermissionMessage = {
|
export const CAMERA_PERMISSION_DENIED: PermissionMessage = {
|
||||||
android: {
|
android: {
|
||||||
@ -19,3 +23,13 @@ export const CAMERA_PERMISSION_DENIED: PermissionMessage = {
|
|||||||
message: "Please go to Settings > Railbird > Camera and grant permissions.",
|
message: "Please go to Settings > Railbird > Camera and grant permissions.",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const PERMISSION_ERROR: ApiError = {
|
||||||
|
title: "There was an issue accessing permissions. ",
|
||||||
|
message: "Please check you settings and try again",
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TERMINATE_UPLOAD_ERROR: ApiError = {
|
||||||
|
title: "There was an issue.",
|
||||||
|
message: "Please try again",
|
||||||
|
};
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
import { Alert, Platform } from "react-native";
|
import { Alert, Platform } from "react-native";
|
||||||
import { CAMERA_PERMISSION_DENIED } from "./constants";
|
import {
|
||||||
|
CAMERA_PERMISSION_DENIED,
|
||||||
|
PERMISSION_ERROR,
|
||||||
|
TERMINATE_UPLOAD_ERROR,
|
||||||
|
} from "./constants";
|
||||||
|
|
||||||
const ALERT_TYPE = {
|
const ALERT_TYPE = {
|
||||||
camera: CAMERA_PERMISSION_DENIED,
|
camera: CAMERA_PERMISSION_DENIED,
|
||||||
|
permissionError: PERMISSION_ERROR,
|
||||||
|
terminateUpload: TERMINATE_UPLOAD_ERROR,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const showAlert = (alertType: string) => {
|
export const showAlert = (alertType: string) => {
|
||||||
const alert = ALERT_TYPE[alertType];
|
const alert = ALERT_TYPE[alertType];
|
||||||
const { title, message } = alert[Platform.OS];
|
const { title, message } = alert[Platform.OS] ?? alert;
|
||||||
Alert.alert(title, message);
|
Alert.alert(title, message);
|
||||||
};
|
};
|
||||||
|
@ -4,10 +4,10 @@ import { Image } from "react-native";
|
|||||||
import CameraScreen from "../component/video/camera";
|
import CameraScreen from "../component/video/camera";
|
||||||
import Profile from "../screens/profile";
|
import Profile from "../screens/profile";
|
||||||
import Session from "../screens/session";
|
import Session from "../screens/session";
|
||||||
import RecordScreen from "../screens/video-stack/record";
|
import VideoDetails from "../screens/video-stack/video-details";
|
||||||
import { tabIconColors } from "../styles";
|
import { tabIconColors } from "../styles";
|
||||||
|
|
||||||
import Icon from "../assets/favicon.png";
|
import Icon from "../assets/icons/favicon.png";
|
||||||
|
|
||||||
const Tab = createBottomTabNavigator();
|
const Tab = createBottomTabNavigator();
|
||||||
const RecordStack = createNativeStackNavigator();
|
const RecordStack = createNativeStackNavigator();
|
||||||
@ -22,8 +22,17 @@ const tabIcons = {
|
|||||||
function VideoTabStack() {
|
function VideoTabStack() {
|
||||||
return (
|
return (
|
||||||
<RecordStack.Navigator screenOptions={{ headerShown: false }}>
|
<RecordStack.Navigator screenOptions={{ headerShown: false }}>
|
||||||
<RecordStack.Screen name="Record" component={RecordScreen} />
|
<RecordStack.Screen
|
||||||
|
name="Record"
|
||||||
|
component={VideoDetails}
|
||||||
|
initialParams={{ mode: "start-video" }}
|
||||||
|
/>
|
||||||
<RecordStack.Screen name="Camera" component={CameraScreen} />
|
<RecordStack.Screen name="Camera" component={CameraScreen} />
|
||||||
|
<RecordStack.Screen
|
||||||
|
name="SaveVideo"
|
||||||
|
component={VideoDetails}
|
||||||
|
initialParams={{ mode: "save-video" }}
|
||||||
|
/>
|
||||||
</RecordStack.Navigator>
|
</RecordStack.Navigator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,161 +0,0 @@
|
|||||||
import React, { useCallback, useState } from "react";
|
|
||||||
import {
|
|
||||||
Keyboard,
|
|
||||||
Text,
|
|
||||||
TextInput,
|
|
||||||
TouchableOpacity,
|
|
||||||
TouchableWithoutFeedback,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import DropDownPicker from "react-native-dropdown-picker";
|
|
||||||
// @ts-ignore
|
|
||||||
import { useCameraPermission } from "react-native-vision-camera";
|
|
||||||
import { showAlert } from "../../lib/alert-messages";
|
|
||||||
import { recordStyles as styles } from "./styles";
|
|
||||||
|
|
||||||
interface CameraScreenParams {
|
|
||||||
gameType: string;
|
|
||||||
tableSize: string;
|
|
||||||
tags: Array<string>;
|
|
||||||
location: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function RecordScreen({ navigation }): React.JSX.Element {
|
|
||||||
// Permissions
|
|
||||||
const { hasPermission, requestPermission } = useCameraPermission();
|
|
||||||
|
|
||||||
if (!hasPermission) {
|
|
||||||
requestPermission();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Game type dropdown
|
|
||||||
const [gameTypeOpen, setGameTypeOpen] = useState<boolean>(false);
|
|
||||||
const [gameType, setGameType] = useState<string | null>(null); // This is a dropdown
|
|
||||||
const [gameTypes, setGameTypes] = useState([
|
|
||||||
{ label: "Free Play", value: "freePlay" },
|
|
||||||
{ label: "Straight Pool", value: "straightPool" },
|
|
||||||
{ label: "Nine Ball", value: "nineBall" },
|
|
||||||
]);
|
|
||||||
const onGameTypeOpen = useCallback(() => {
|
|
||||||
setTableSizeOpen(false);
|
|
||||||
setTagsOpen(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Table size dropdown
|
|
||||||
const [tableSizeOpen, setTableSizeOpen] = useState<boolean>(false);
|
|
||||||
const [tableSize, setTableSize] = useState<string>("");
|
|
||||||
const [tableSizes, setTableSizes] = useState([
|
|
||||||
{ label: `9'`, value: "nineFoot" },
|
|
||||||
{ label: `8'`, value: "eightFoot" },
|
|
||||||
{ label: "7", value: "sevenFoot" },
|
|
||||||
]);
|
|
||||||
const onTableSizeOpen = useCallback(() => {
|
|
||||||
setGameTypeOpen(false);
|
|
||||||
setTagsOpen(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Tags multi-select dropdown
|
|
||||||
const [tagsOpen, setTagsOpen] = useState<boolean>(false);
|
|
||||||
const [tags, setTags] = useState<Array<string>>([]);
|
|
||||||
const [tagsList, setTagsList] = useState([
|
|
||||||
{ label: `Tag1`, value: "tag1" },
|
|
||||||
{ label: `Tag2`, value: "tag2" },
|
|
||||||
{ label: "Tag3", value: "tag3" },
|
|
||||||
]);
|
|
||||||
const onTagsOpen = useCallback(() => {
|
|
||||||
setTableSizeOpen(false);
|
|
||||||
setGameTypeOpen(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Location
|
|
||||||
const [location, setLocation] = useState<string>("");
|
|
||||||
|
|
||||||
const handleSubmit = () => {
|
|
||||||
if (!hasPermission) {
|
|
||||||
return showAlert("camera");
|
|
||||||
}
|
|
||||||
|
|
||||||
// needs to pass info as params or store in a context/state provider
|
|
||||||
const params: CameraScreenParams = {
|
|
||||||
gameType: gameType,
|
|
||||||
tableSize: tableSize,
|
|
||||||
tags: tags,
|
|
||||||
location: location,
|
|
||||||
};
|
|
||||||
navigation.push("Camera", params);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dropDownStyles = {
|
|
||||||
style: styles.dropdownStyle,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
|
|
||||||
<View style={styles.container}>
|
|
||||||
<View style={styles.dropdownContainer}>
|
|
||||||
<Text style={styles.dropdownTitle}>Game Type</Text>
|
|
||||||
<DropDownPicker
|
|
||||||
zIndex={3000}
|
|
||||||
zIndexInverse={1000}
|
|
||||||
open={gameTypeOpen}
|
|
||||||
value={gameType}
|
|
||||||
items={gameTypes}
|
|
||||||
setOpen={setGameTypeOpen}
|
|
||||||
setValue={setGameType}
|
|
||||||
setItems={setGameTypes}
|
|
||||||
onOpen={onGameTypeOpen}
|
|
||||||
{...dropDownStyles}
|
|
||||||
/>
|
|
||||||
<Text style={styles.dropdownTitle}>Table size</Text>
|
|
||||||
<DropDownPicker
|
|
||||||
zIndex={2000}
|
|
||||||
zIndexInverse={2000}
|
|
||||||
open={tableSizeOpen}
|
|
||||||
value={tableSize}
|
|
||||||
items={tableSizes}
|
|
||||||
setOpen={setTableSizeOpen}
|
|
||||||
setValue={setTableSize}
|
|
||||||
setItems={setTableSizes}
|
|
||||||
onOpen={onTableSizeOpen}
|
|
||||||
{...dropDownStyles}
|
|
||||||
/>
|
|
||||||
<Text style={styles.dropdownTitle}>Tags</Text>
|
|
||||||
<DropDownPicker
|
|
||||||
zIndex={1000}
|
|
||||||
zIndexInverse={3000}
|
|
||||||
multiple
|
|
||||||
min={0}
|
|
||||||
max={5}
|
|
||||||
open={tagsOpen}
|
|
||||||
value={tags}
|
|
||||||
items={tagsList}
|
|
||||||
setOpen={setTagsOpen}
|
|
||||||
setValue={setTags}
|
|
||||||
setItems={setTagsList}
|
|
||||||
onOpen={onTagsOpen}
|
|
||||||
{...dropDownStyles}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<Text style={styles.dropdownTitle}>Location</Text>
|
|
||||||
<TextInput
|
|
||||||
style={styles.input}
|
|
||||||
placeholder="Location"
|
|
||||||
value={location}
|
|
||||||
onChangeText={(value) => setLocation(value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<View style={styles.buttonContainer}>
|
|
||||||
<TouchableOpacity
|
|
||||||
style={styles.buttonStyle}
|
|
||||||
onPress={() => navigation.goBack()}
|
|
||||||
>
|
|
||||||
<Text style={styles.buttonText}>Back</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TouchableOpacity style={styles.buttonStyle} onPress={handleSubmit}>
|
|
||||||
<Text style={styles.buttonText}>Next</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</TouchableWithoutFeedback>
|
|
||||||
);
|
|
||||||
}
|
|
@ -7,26 +7,13 @@ export const recordStyles = StyleSheet.create({
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
padding: 20,
|
padding: 20,
|
||||||
},
|
},
|
||||||
dropdownContainer: {
|
headerText: {
|
||||||
width: "100%",
|
fontSize: 22,
|
||||||
marginBottom: 20,
|
|
||||||
zIndex: 50,
|
|
||||||
},
|
|
||||||
dropdownTitle: {
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: "500",
|
fontWeight: "500",
|
||||||
marginBottom: 5,
|
paddingBottom: "10%",
|
||||||
alignSelf: "flex-start",
|
|
||||||
},
|
|
||||||
input: {
|
|
||||||
width: "100%",
|
|
||||||
marginBottom: 20,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "grey",
|
|
||||||
borderRadius: 5,
|
|
||||||
padding: 10,
|
|
||||||
},
|
},
|
||||||
buttonContainer: {
|
buttonContainer: {
|
||||||
|
marginTop: 10,
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
@ -42,16 +29,4 @@ export const recordStyles = StyleSheet.create({
|
|||||||
color: "white",
|
color: "white",
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
},
|
},
|
||||||
dropdownStyle: {
|
|
||||||
backgroundColor: "#ffffff",
|
|
||||||
borderColor: "#D1D1D1",
|
|
||||||
borderWidth: 1,
|
|
||||||
borderRadius: 4,
|
|
||||||
},
|
|
||||||
dropdownContainerStyle: {
|
|
||||||
marginBottom: 10,
|
|
||||||
borderColor: "#D1D1D1",
|
|
||||||
borderWidth: 1,
|
|
||||||
borderRadius: 4,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
94
src/screens/video-stack/video-details.tsx
Normal file
94
src/screens/video-stack/video-details.tsx
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Keyboard,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
TouchableOpacity,
|
||||||
|
TouchableWithoutFeedback,
|
||||||
|
View,
|
||||||
|
} from "react-native";
|
||||||
|
import DropDownPicker from "react-native-dropdown-picker";
|
||||||
|
import { useVideoDetails } from "../../component/video/use-video-details";
|
||||||
|
import { globalInputStyles } from "../../styles";
|
||||||
|
import { recordStyles as styles } from "./styles";
|
||||||
|
|
||||||
|
export default function VideoDetails({ navigation, route }): React.JSX.Element {
|
||||||
|
const { mode } = route.params;
|
||||||
|
const {
|
||||||
|
sessionName,
|
||||||
|
setSessionName,
|
||||||
|
gameType,
|
||||||
|
tableSize,
|
||||||
|
handleSubmit,
|
||||||
|
loading,
|
||||||
|
} = useVideoDetails({ params: route.params, navigation });
|
||||||
|
|
||||||
|
const dropDownStyles = {
|
||||||
|
style: globalInputStyles.dropdownStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.headerText}>
|
||||||
|
{mode === "start-video" ? "Record Session" : "Save Session"}
|
||||||
|
</Text>
|
||||||
|
<View style={globalInputStyles.dropdownContainer}>
|
||||||
|
<Text style={globalInputStyles.dropdownTitle}>Session Name</Text>
|
||||||
|
<TextInput
|
||||||
|
style={globalInputStyles.input}
|
||||||
|
placeholder="Session name"
|
||||||
|
autoCapitalize="none"
|
||||||
|
value={sessionName}
|
||||||
|
onChangeText={setSessionName}
|
||||||
|
/>
|
||||||
|
<Text style={globalInputStyles.dropdownTitle}>Game Type</Text>
|
||||||
|
<DropDownPicker
|
||||||
|
zIndex={3000}
|
||||||
|
zIndexInverse={1000}
|
||||||
|
open={gameType.isDropdownOpen}
|
||||||
|
value={gameType.value}
|
||||||
|
items={gameType.optionsList}
|
||||||
|
setOpen={gameType.toggleOpen}
|
||||||
|
setValue={gameType.setValue}
|
||||||
|
setItems={gameType.setOptionsList}
|
||||||
|
{...dropDownStyles}
|
||||||
|
/>
|
||||||
|
<Text style={globalInputStyles.dropdownTitle}>Table Size</Text>
|
||||||
|
<DropDownPicker
|
||||||
|
zIndex={2000}
|
||||||
|
zIndexInverse={2000}
|
||||||
|
open={tableSize.isDropdownOpen}
|
||||||
|
value={tableSize.value}
|
||||||
|
items={tableSize.optionsList}
|
||||||
|
setOpen={tableSize.toggleOpen}
|
||||||
|
setValue={tableSize.setValue}
|
||||||
|
setItems={tableSize.setOptionsList}
|
||||||
|
{...dropDownStyles}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.buttonContainer}>
|
||||||
|
{mode === "start-video" && (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.buttonStyle}
|
||||||
|
onPress={() => navigation.goBack()}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>Back</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
<TouchableOpacity style={styles.buttonStyle} onPress={handleSubmit}>
|
||||||
|
{loading ? (
|
||||||
|
<ActivityIndicator color="#fff" />
|
||||||
|
) : (
|
||||||
|
<Text style={styles.buttonText}>
|
||||||
|
{mode === "start-video" ? "Next" : "Save"}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
);
|
||||||
|
}
|
@ -1,4 +1,33 @@
|
|||||||
// GLOBAL STYLES
|
// GLOBAL STYLES
|
||||||
|
import { StyleSheet } from "react-native";
|
||||||
|
|
||||||
|
export const globalInputStyles = StyleSheet.create({
|
||||||
|
input: {
|
||||||
|
width: "100%",
|
||||||
|
padding: 10,
|
||||||
|
backgroundColor: "#ffffff",
|
||||||
|
borderColor: "#D1D1D1",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
dropdownContainer: {
|
||||||
|
width: "100%",
|
||||||
|
marginBottom: 20,
|
||||||
|
zIndex: 50,
|
||||||
|
},
|
||||||
|
dropdownTitle: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "500",
|
||||||
|
marginBottom: 5,
|
||||||
|
alignSelf: "flex-start",
|
||||||
|
},
|
||||||
|
dropdownStyle: {
|
||||||
|
backgroundColor: "#ffffff",
|
||||||
|
borderColor: "#D1D1D1",
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
});
|
||||||
// COLORS:
|
// COLORS:
|
||||||
// can be made more granular to specify utility (ex: fontColors vs backgroundColors)
|
// can be made more granular to specify utility (ex: fontColors vs backgroundColors)
|
||||||
export const colors = {
|
export const colors = {
|
||||||
|
@ -9800,7 +9800,7 @@ queue@6.0.2:
|
|||||||
|
|
||||||
"railbird-gql@git+https://dev.railbird.ai/railbird/railbird-gql.git#master":
|
"railbird-gql@git+https://dev.railbird.ai/railbird/railbird-gql.git#master":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "git+https://dev.railbird.ai/railbird/railbird-gql.git#204e289627b08a96b947b6f23ed4a843d9e8abff"
|
resolved "git+https://dev.railbird.ai/railbird/railbird-gql.git#ce8cfd6a68d7b27f478c99dd5ae74a411d4d0c77"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@apollo/client" "^3.9.2"
|
"@apollo/client" "^3.9.2"
|
||||||
"@graphql-codegen/cli" "^5.0.0"
|
"@graphql-codegen/cli" "^5.0.0"
|
||||||
|
Loading…
Reference in New Issue
Block a user