Merge branch 'master' into loewy/session-details
This commit is contained in:
commit
608e784205
@ -155,7 +155,7 @@ export default function CameraScreen({
|
|||||||
const onMediaCaptured = useCallback(
|
const onMediaCaptured = useCallback(
|
||||||
(media: PhotoFile | VideoFile) => {
|
(media: PhotoFile | VideoFile) => {
|
||||||
console.log(`Media captured! ${JSON.stringify(media)}`);
|
console.log(`Media captured! ${JSON.stringify(media)}`);
|
||||||
navigation.push("SaveVideo", {
|
navigation.push("SaveRecording", {
|
||||||
videoId: uploadManager?.videoId,
|
videoId: uploadManager?.videoId,
|
||||||
...params,
|
...params,
|
||||||
});
|
});
|
@ -54,7 +54,7 @@ function useDropdown<T>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useVideoDetails = ({
|
export const useRecordingDetails = ({
|
||||||
params: {
|
params: {
|
||||||
mode,
|
mode,
|
||||||
videoId,
|
videoId,
|
||||||
@ -118,7 +118,7 @@ export const useVideoDetails = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Navigate if starting flow, terminateUploadStream if completing flow
|
// Navigate if starting flow, terminateUploadStream if completing flow
|
||||||
if (mode === "start-video") {
|
if (mode === "start-recording") {
|
||||||
const params: VideoFlowInputParams = {
|
const params: VideoFlowInputParams = {
|
||||||
sessionName,
|
sessionName,
|
||||||
gameType: gameType.value,
|
gameType: gameType.value,
|
26
src/component/video-card/video-card-footer.tsx
Normal file
26
src/component/video-card/video-card-footer.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { StyleSheet, Text, View } from "react-native";
|
||||||
|
|
||||||
|
const VideoCardFooter = ({ videoName, lastPlayed }) => {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text style={styles.videoName}>{videoName}</Text>
|
||||||
|
<Text style={styles.videoDatetime}>{lastPlayed}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
videoName: {
|
||||||
|
fontSize: 18,
|
||||||
|
paddingTop: 5,
|
||||||
|
marginHorizontal: 16,
|
||||||
|
},
|
||||||
|
videoDatetime: {
|
||||||
|
fontSize: 10,
|
||||||
|
color: "#A3A3A3",
|
||||||
|
marginHorizontal: 16,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default VideoCardFooter;
|
68
src/component/video-card/video-card-header.tsx
Normal file
68
src/component/video-card/video-card-header.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Image, StyleSheet, Text, View } from "react-native";
|
||||||
|
|
||||||
|
const VideoCardHeader = ({
|
||||||
|
playerName,
|
||||||
|
location,
|
||||||
|
gameType,
|
||||||
|
locationIconURL,
|
||||||
|
profileImageURL,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.headerContainer}>
|
||||||
|
<Image
|
||||||
|
style={styles.headerProfileImage}
|
||||||
|
source={{ uri: profileImageURL }}
|
||||||
|
accessibilityLabel="Profile image"
|
||||||
|
/>
|
||||||
|
<View style={styles.headerText}>
|
||||||
|
<Text style={styles.playerName}>{playerName}</Text>
|
||||||
|
<View style={styles.locationContainer}>
|
||||||
|
<Image source={{ uri: locationIconURL }} style={styles.icon} />
|
||||||
|
<Text style={styles.locationText}>{location}</Text>
|
||||||
|
</View>
|
||||||
|
<Text style={styles.gameType}>{gameType}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
headerContainer: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
marginHorizontal: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
headerProfileImage: {
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
resizeMode: "contain",
|
||||||
|
},
|
||||||
|
|
||||||
|
headerText: {
|
||||||
|
flexDirection: "column",
|
||||||
|
padding: 10,
|
||||||
|
},
|
||||||
|
playerName: {
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
locationContainer: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
locationText: {
|
||||||
|
fontSize: 13,
|
||||||
|
},
|
||||||
|
gameType: {
|
||||||
|
fontSize: 13,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
resizeMode: "contain",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default VideoCardHeader;
|
25
src/component/video-card/video-card-stat.tsx
Normal file
25
src/component/video-card/video-card-stat.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { StyleSheet, Text, View } from "react-native";
|
||||||
|
|
||||||
|
const VideoCardStat = ({ videoStat, displayName }) => {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text style={styles.statItem}>{displayName}</Text>
|
||||||
|
<Text style={styles.statValue}>{videoStat}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
statItem: {
|
||||||
|
fontSize: 10,
|
||||||
|
textAlign: "center",
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
|
statValue: {
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: "400",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default VideoCardStat;
|
48
src/component/video-card/video-card-stats-row-container.tsx
Normal file
48
src/component/video-card/video-card-stats-row-container.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { StyleSheet, View } from "react-native";
|
||||||
|
import VideoCardStat from "./video-card-stat";
|
||||||
|
|
||||||
|
const VideoCardStatsRowContainer = ({
|
||||||
|
makePercent,
|
||||||
|
medianRun,
|
||||||
|
duration,
|
||||||
|
shotPacing,
|
||||||
|
}) => {
|
||||||
|
const stats = [
|
||||||
|
{ displayName: "Make Percent", videoStat: makePercent },
|
||||||
|
{ displayName: "Median Run", videoStat: medianRun },
|
||||||
|
{ displayName: "Time Played", videoStat: duration },
|
||||||
|
{ displayName: "Shot Pacing", videoStat: shotPacing },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.statsContainer}>
|
||||||
|
{stats.map((stat, index) => (
|
||||||
|
<React.Fragment key={index}>
|
||||||
|
<VideoCardStat
|
||||||
|
displayName={stat.displayName}
|
||||||
|
videoStat={stat.videoStat}
|
||||||
|
/>
|
||||||
|
{index < stats.length - 1 && <View style={styles.verticalSpacer} />}
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
statsContainer: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
margin: 16,
|
||||||
|
},
|
||||||
|
verticalSpacer: {
|
||||||
|
width: 1,
|
||||||
|
backgroundColor: "#A3A3A3",
|
||||||
|
height: "100%",
|
||||||
|
marginHorizontal: 12,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default VideoCardStatsRowContainer;
|
65
src/component/video-card/video-card.tsx
Normal file
65
src/component/video-card/video-card.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Image, StyleSheet, View } from "react-native";
|
||||||
|
import VideoCardFooter from "./video-card-footer";
|
||||||
|
import VideoCardHeader from "./video-card-header";
|
||||||
|
import VideoCardStatsRowContainer from "./video-card-stats-row-container";
|
||||||
|
|
||||||
|
const VideoCard = ({
|
||||||
|
playerName,
|
||||||
|
location,
|
||||||
|
gameType,
|
||||||
|
makePercent,
|
||||||
|
medianRun,
|
||||||
|
duration,
|
||||||
|
shotPacing,
|
||||||
|
videoName,
|
||||||
|
lastPlayed,
|
||||||
|
imageURL,
|
||||||
|
profileImageURL,
|
||||||
|
locationIconURL,
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<View style={styles.card}>
|
||||||
|
<VideoCardHeader
|
||||||
|
playerName={playerName}
|
||||||
|
location={location}
|
||||||
|
gameType={gameType}
|
||||||
|
locationIconURL={locationIconURL}
|
||||||
|
profileImageURL={profileImageURL}
|
||||||
|
/>
|
||||||
|
<VideoCardStatsRowContainer
|
||||||
|
makePercent={makePercent}
|
||||||
|
medianRun={medianRun}
|
||||||
|
duration={duration}
|
||||||
|
shotPacing={shotPacing}
|
||||||
|
/>
|
||||||
|
<Image source={imageURL} style={styles.image} />
|
||||||
|
<VideoCardFooter videoName={videoName} lastPlayed={lastPlayed} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
card: {
|
||||||
|
backgroundColor: "white",
|
||||||
|
borderRadius: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#ddd",
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: {
|
||||||
|
width: 0,
|
||||||
|
height: 2,
|
||||||
|
},
|
||||||
|
shadowOpacity: 0.25,
|
||||||
|
shadowRadius: 3.84,
|
||||||
|
elevation: 5,
|
||||||
|
margin: 10,
|
||||||
|
overflow: "hidden",
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: "100%",
|
||||||
|
height: "50%",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default VideoCard;
|
@ -1,11 +1,11 @@
|
|||||||
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
|
||||||
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
import { createNativeStackNavigator } from "@react-navigation/native-stack";
|
||||||
import { Image } from "react-native";
|
import { Image } from "react-native";
|
||||||
import CameraScreen from "../component/video/camera";
|
import CameraScreen from "../component/recording/camera";
|
||||||
import Profile from "../screens/profile";
|
import Profile from "../screens/profile";
|
||||||
import Session from "../screens/session-stack/session";
|
import RecordingDetails from "../screens/recording-stack/recording-details";
|
||||||
import SessionFeed from "../screens/session-stack/session-feed";
|
import Video from "../screens/video-stack/video-details";
|
||||||
import VideoDetails from "../screens/video-stack/video-details";
|
import VideoFeed from "../screens/video-stack/video-feed";
|
||||||
import { tabIconColors } from "../styles";
|
import { tabIconColors } from "../styles";
|
||||||
|
|
||||||
import Icon from "../assets/icons/favicon.png";
|
import Icon from "../assets/icons/favicon.png";
|
||||||
@ -15,34 +15,34 @@ const RecordStack = createNativeStackNavigator();
|
|||||||
|
|
||||||
// tabBarIcon configuration should live on separate file and contain all logic/icons/rendering for the Tabs
|
// tabBarIcon configuration should live on separate file and contain all logic/icons/rendering for the Tabs
|
||||||
const tabIcons = {
|
const tabIcons = {
|
||||||
SessionStack: <Image source={Icon} style={{ width: 20, height: 20 }} />,
|
|
||||||
VideoStack: <Image source={Icon} style={{ width: 20, height: 20 }} />,
|
VideoStack: <Image source={Icon} style={{ width: 20, height: 20 }} />,
|
||||||
|
RecordingStack: <Image source={Icon} style={{ width: 20, height: 20 }} />,
|
||||||
Profile: <Image source={Icon} style={{ width: 20, height: 20 }} />,
|
Profile: <Image source={Icon} style={{ width: 20, height: 20 }} />,
|
||||||
};
|
};
|
||||||
|
|
||||||
function VideoTabStack() {
|
function RecordingTabStack() {
|
||||||
return (
|
return (
|
||||||
<RecordStack.Navigator screenOptions={{ headerShown: false }}>
|
<RecordStack.Navigator screenOptions={{ headerShown: false }}>
|
||||||
<RecordStack.Screen
|
<RecordStack.Screen
|
||||||
name="Record"
|
name="Record"
|
||||||
component={VideoDetails}
|
component={RecordingDetails}
|
||||||
initialParams={{ mode: "start-video" }}
|
initialParams={{ mode: "start-recording" }}
|
||||||
/>
|
/>
|
||||||
<RecordStack.Screen name="Camera" component={CameraScreen} />
|
<RecordStack.Screen name="Camera" component={CameraScreen} />
|
||||||
<RecordStack.Screen
|
<RecordStack.Screen
|
||||||
name="SaveVideo"
|
name="SaveRecording"
|
||||||
component={VideoDetails}
|
component={RecordingDetails}
|
||||||
initialParams={{ mode: "save-video" }}
|
initialParams={{ mode: "save-recording" }}
|
||||||
/>
|
/>
|
||||||
</RecordStack.Navigator>
|
</RecordStack.Navigator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SessionTabStack() {
|
function VideoTabStack() {
|
||||||
return (
|
return (
|
||||||
<RecordStack.Navigator screenOptions={{ headerShown: false }}>
|
<RecordStack.Navigator screenOptions={{ headerShown: false }}>
|
||||||
<RecordStack.Screen name="SessionFeed" component={SessionFeed} />
|
<RecordStack.Screen name="VideoFeed" component={VideoFeed} />
|
||||||
<RecordStack.Screen name="Session" component={Session} />
|
<RecordStack.Screen name="Video" component={Video} />
|
||||||
</RecordStack.Navigator>
|
</RecordStack.Navigator>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -67,14 +67,14 @@ export default function Tabs(): React.JSX.Element {
|
|||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Tab.Screen
|
|
||||||
name="SessionStack"
|
|
||||||
component={SessionTabStack}
|
|
||||||
options={{ tabBarLabel: "Session" }}
|
|
||||||
/>
|
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
name="VideoStack"
|
name="VideoStack"
|
||||||
component={VideoTabStack}
|
component={VideoTabStack}
|
||||||
|
options={{ tabBarLabel: "Feed" }}
|
||||||
|
/>
|
||||||
|
<Tab.Screen
|
||||||
|
name="RecordingStack"
|
||||||
|
component={RecordingTabStack}
|
||||||
options={{ tabBarLabel: "Record" }}
|
options={{ tabBarLabel: "Record" }}
|
||||||
/>
|
/>
|
||||||
<Tab.Screen
|
<Tab.Screen
|
||||||
|
94
src/screens/recording-stack/recording-details.tsx
Normal file
94
src/screens/recording-stack/recording-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 { useRecordingDetails } from "../../component/recording/use-recording-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,
|
||||||
|
} = useRecordingDetails({ params: route.params, navigation });
|
||||||
|
|
||||||
|
const dropDownStyles = {
|
||||||
|
style: globalInputStyles.dropdownStyle,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.headerText}>
|
||||||
|
{mode === "start-recording" ? "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-recording" && (
|
||||||
|
<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-recording" ? "Next" : "Save"}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</TouchableWithoutFeedback>
|
||||||
|
);
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import VideoDetails from "./video-details";
|
|
||||||
|
|
||||||
export default function SessionScreen({ navigation }) {
|
|
||||||
return <VideoDetails navigation={navigation} />;
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { ScrollView, StyleSheet, Text, View } from "react-native";
|
|
||||||
import {
|
|
||||||
graph_data_two_measures,
|
|
||||||
mock_session_details,
|
|
||||||
} from "../../../test/mock/charts/mock-data";
|
|
||||||
import BarGraph from "../../component/charts/bar-graph/bar-graph";
|
|
||||||
import ChartContainer from "../../component/charts/container/chart-container";
|
|
||||||
import ImageWithFallback from "../../component/image/image-with-fallback";
|
|
||||||
import StatList from "../../component/video-details/video-stat-list";
|
|
||||||
|
|
||||||
// TODO: #134 remove Session when data piped through
|
|
||||||
// Splash should be an asset we use if an Image failed to load
|
|
||||||
import Session from "../../assets/sample_session.png";
|
|
||||||
import Splash from "../../assets/splash.png";
|
|
||||||
import BackHeader from "../../component/headers/back-header";
|
|
||||||
import { borders, colors } from "../../styles";
|
|
||||||
|
|
||||||
export default function VideoDetails({ navigation }) {
|
|
||||||
// TODO: #134 Remove mock destructure block when data is piped through from BE
|
|
||||||
const {
|
|
||||||
sessionTitle,
|
|
||||||
date,
|
|
||||||
timePlayed,
|
|
||||||
medianRun,
|
|
||||||
makeRate,
|
|
||||||
shotPacing,
|
|
||||||
gameType,
|
|
||||||
notes,
|
|
||||||
} = mock_session_details;
|
|
||||||
|
|
||||||
const leftColumnStats = [
|
|
||||||
{ title: "TIME PLAYED", value: timePlayed },
|
|
||||||
{ title: "MAKE RATE", value: makeRate },
|
|
||||||
];
|
|
||||||
|
|
||||||
const rightColumnStats = [
|
|
||||||
{ title: "MEDIAN RUN", value: medianRun },
|
|
||||||
{ title: "SHOT PACING", value: shotPacing },
|
|
||||||
];
|
|
||||||
// End mock destructure
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<BackHeader navigation={navigation} title="Go Back" />
|
|
||||||
<ScrollView
|
|
||||||
showsVerticalScrollIndicator={false}
|
|
||||||
contentContainerStyle={styles.scrollContainer}
|
|
||||||
>
|
|
||||||
<View style={styles.headerSection}>
|
|
||||||
<Text style={styles.header}>{sessionTitle}</Text>
|
|
||||||
<Text>{date}</Text>
|
|
||||||
</View>
|
|
||||||
<ImageWithFallback
|
|
||||||
// TODO: #134 when data comes from be needs to be passed as source={{ uri: image }}
|
|
||||||
source={Session}
|
|
||||||
fallbackSource={Splash}
|
|
||||||
style={styles.image}
|
|
||||||
/>
|
|
||||||
<View style={styles.statsContainer}>
|
|
||||||
<StatList items={leftColumnStats} style={styles.statColumn} />
|
|
||||||
<StatList items={rightColumnStats} style={styles.statColumn} />
|
|
||||||
</View>
|
|
||||||
<View style={styles.horizontalDivider} />
|
|
||||||
<View style={styles.textContainer}>
|
|
||||||
<Text style={styles.title}>Game Type</Text>
|
|
||||||
<Text style={styles.text}>{gameType}</Text>
|
|
||||||
<Text style={styles.title}>Notes</Text>
|
|
||||||
<Text style={styles.text}>{notes}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={[styles.horizontalDivider, { marginVertical: 21 }]} />
|
|
||||||
<ChartContainer
|
|
||||||
data={graph_data_two_measures}
|
|
||||||
ChartComponent={BarGraph}
|
|
||||||
/>
|
|
||||||
</ScrollView>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: #130 scaled styles + maintain consistency with video-feed styles
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
scrollContainer: {
|
|
||||||
backgroundColor: "white", // TODO #125 -- this color should not be set but implicitly inherit from theme
|
|
||||||
paddingBottom: 20, // guarantees some space at bottom of scrollable views
|
|
||||||
},
|
|
||||||
headerSection: {
|
|
||||||
paddingHorizontal: 38,
|
|
||||||
paddingTop: 17,
|
|
||||||
paddingBottom: 14,
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: "bold",
|
|
||||||
},
|
|
||||||
image: {
|
|
||||||
width: "100%",
|
|
||||||
height: 248,
|
|
||||||
},
|
|
||||||
statsContainer: {
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
paddingLeft: 45,
|
|
||||||
paddingVertical: 42,
|
|
||||||
},
|
|
||||||
statColumn: {
|
|
||||||
flex: 1,
|
|
||||||
padding: 5,
|
|
||||||
},
|
|
||||||
horizontalDivider: {
|
|
||||||
width: "95%",
|
|
||||||
alignSelf: "center",
|
|
||||||
...borders.dottedBottomBorder,
|
|
||||||
},
|
|
||||||
textContainer: {
|
|
||||||
paddingHorizontal: 38,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
fontSize: 16,
|
|
||||||
color: colors.text.greyText,
|
|
||||||
paddingTop: 16,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
fontSize: 18,
|
|
||||||
textAlign: "left",
|
|
||||||
paddingTop: 6,
|
|
||||||
fontWeight: "400",
|
|
||||||
},
|
|
||||||
});
|
|
@ -1,94 +1,129 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { ScrollView, StyleSheet, Text, View } from "react-native";
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
graph_data_two_measures,
|
||||||
Keyboard,
|
mock_session_details,
|
||||||
Text,
|
} from "../../../test/mock/charts/mock-data";
|
||||||
TextInput,
|
import BarGraph from "../../component/charts/bar-graph/bar-graph";
|
||||||
TouchableOpacity,
|
import ChartContainer from "../../component/charts/container/chart-container";
|
||||||
TouchableWithoutFeedback,
|
import ImageWithFallback from "../../component/image/image-with-fallback";
|
||||||
View,
|
import StatList from "../../component/video-details/video-stat-list";
|
||||||
} 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 {
|
// TODO: #134 remove Session when data piped through
|
||||||
const { mode } = route.params;
|
// Splash should be an asset we use if an Image failed to load
|
||||||
|
import Session from "../../assets/sample_session.png";
|
||||||
|
import Splash from "../../assets/splash.png";
|
||||||
|
import BackHeader from "../../component/headers/back-header";
|
||||||
|
import { borders, colors } from "../../styles";
|
||||||
|
|
||||||
|
export default function VideoDetails({ navigation }) {
|
||||||
|
// TODO: #134 Remove mock destructure block when data is piped through from BE
|
||||||
const {
|
const {
|
||||||
sessionName,
|
sessionTitle,
|
||||||
setSessionName,
|
date,
|
||||||
|
timePlayed,
|
||||||
|
medianRun,
|
||||||
|
makeRate,
|
||||||
|
shotPacing,
|
||||||
gameType,
|
gameType,
|
||||||
tableSize,
|
notes,
|
||||||
handleSubmit,
|
} = mock_session_details;
|
||||||
loading,
|
|
||||||
} = useVideoDetails({ params: route.params, navigation });
|
|
||||||
|
|
||||||
const dropDownStyles = {
|
const leftColumnStats = [
|
||||||
style: globalInputStyles.dropdownStyle,
|
{ title: "TIME PLAYED", value: timePlayed },
|
||||||
};
|
{ title: "MAKE RATE", value: makeRate },
|
||||||
|
];
|
||||||
|
|
||||||
|
const rightColumnStats = [
|
||||||
|
{ title: "MEDIAN RUN", value: medianRun },
|
||||||
|
{ title: "SHOT PACING", value: shotPacing },
|
||||||
|
];
|
||||||
|
// End mock destructure
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
|
<>
|
||||||
<View style={styles.container}>
|
<BackHeader navigation={navigation} title="Go Back" />
|
||||||
<Text style={styles.headerText}>
|
<ScrollView
|
||||||
{mode === "start-video" ? "Record Session" : "Save Session"}
|
showsVerticalScrollIndicator={false}
|
||||||
</Text>
|
contentContainerStyle={styles.scrollContainer}
|
||||||
<View style={globalInputStyles.dropdownContainer}>
|
>
|
||||||
<Text style={globalInputStyles.dropdownTitle}>Session Name</Text>
|
<View style={styles.headerSection}>
|
||||||
<TextInput
|
<Text style={styles.header}>{sessionTitle}</Text>
|
||||||
style={globalInputStyles.input}
|
<Text>{date}</Text>
|
||||||
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>
|
||||||
|
<ImageWithFallback
|
||||||
<View style={styles.buttonContainer}>
|
// TODO: #134 when data comes from be needs to be passed as source={{ uri: image }}
|
||||||
{mode === "start-video" && (
|
source={Session}
|
||||||
<TouchableOpacity
|
fallbackSource={Splash}
|
||||||
style={styles.buttonStyle}
|
style={styles.image}
|
||||||
onPress={() => navigation.goBack()}
|
/>
|
||||||
>
|
<View style={styles.statsContainer}>
|
||||||
<Text style={styles.buttonText}>Back</Text>
|
<StatList items={leftColumnStats} style={styles.statColumn} />
|
||||||
</TouchableOpacity>
|
<StatList items={rightColumnStats} style={styles.statColumn} />
|
||||||
)}
|
|
||||||
<TouchableOpacity style={styles.buttonStyle} onPress={handleSubmit}>
|
|
||||||
{loading ? (
|
|
||||||
<ActivityIndicator color="#fff" />
|
|
||||||
) : (
|
|
||||||
<Text style={styles.buttonText}>
|
|
||||||
{mode === "start-video" ? "Next" : "Save"}
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
<View style={styles.horizontalDivider} />
|
||||||
</TouchableWithoutFeedback>
|
<View style={styles.textContainer}>
|
||||||
|
<Text style={styles.title}>Game Type</Text>
|
||||||
|
<Text style={styles.text}>{gameType}</Text>
|
||||||
|
<Text style={styles.title}>Notes</Text>
|
||||||
|
<Text style={styles.text}>{notes}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={[styles.horizontalDivider, { marginVertical: 21 }]} />
|
||||||
|
<ChartContainer
|
||||||
|
data={graph_data_two_measures}
|
||||||
|
ChartComponent={BarGraph}
|
||||||
|
/>
|
||||||
|
</ScrollView>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: #130 scaled styles + maintain consistency with video-feed styles
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
scrollContainer: {
|
||||||
|
backgroundColor: "white", // TODO #125 -- this color should not be set but implicitly inherit from theme
|
||||||
|
paddingBottom: 20, // guarantees some space at bottom of scrollable views
|
||||||
|
},
|
||||||
|
headerSection: {
|
||||||
|
paddingHorizontal: 38,
|
||||||
|
paddingTop: 17,
|
||||||
|
paddingBottom: 14,
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
image: {
|
||||||
|
width: "100%",
|
||||||
|
height: 248,
|
||||||
|
},
|
||||||
|
statsContainer: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
paddingLeft: 45,
|
||||||
|
paddingVertical: 42,
|
||||||
|
},
|
||||||
|
statColumn: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 5,
|
||||||
|
},
|
||||||
|
horizontalDivider: {
|
||||||
|
width: "95%",
|
||||||
|
alignSelf: "center",
|
||||||
|
...borders.dottedBottomBorder,
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
paddingHorizontal: 38,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: colors.text.greyText,
|
||||||
|
paddingTop: 16,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: 18,
|
||||||
|
textAlign: "left",
|
||||||
|
paddingTop: 6,
|
||||||
|
fontWeight: "400",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
import { StackNavigationProp } from "@react-navigation/stack";
|
import { StackNavigationProp } from "@react-navigation/stack";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { StyleSheet, TouchableOpacity, View } from "react-native";
|
import { StyleSheet, TouchableOpacity, View } from "react-native";
|
||||||
import sampleSessionImage from "../../assets/sample_session.png";
|
import sampleVideoImage from "../../assets/sample_session.png";
|
||||||
import SessionCard from "../../component/session-card/session-card";
|
import VideoCard from "../../component/video-card/video-card";
|
||||||
|
|
||||||
// Define the types for your navigation stack
|
// Define the types for your navigation stack
|
||||||
type SessionStackParamList = {
|
type VideoStackParamList = {
|
||||||
Session: undefined; // Add other screens as needed
|
Video: undefined; // Add other screens as needed
|
||||||
};
|
};
|
||||||
|
|
||||||
type SessionFeedNavigationProp = StackNavigationProp<
|
type VideoFeedNavigationProp = StackNavigationProp<
|
||||||
SessionStackParamList,
|
VideoStackParamList,
|
||||||
"Session"
|
"Video"
|
||||||
>;
|
>;
|
||||||
|
|
||||||
// Define the props for SessionFeed component
|
// Define the props for VideoFeed component
|
||||||
interface SessionFeedProps {
|
interface VideoFeedProps {
|
||||||
navigation: SessionFeedNavigationProp;
|
navigation: VideoFeedNavigationProp;
|
||||||
}
|
}
|
||||||
const SessionFeed: React.FC<SessionFeedProps> = ({ navigation }) => {
|
const VideoFeed: React.FC<VideoFeedProps> = ({ navigation }) => {
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<TouchableOpacity onPress={() => navigation.push("Session")}>
|
<TouchableOpacity onPress={() => navigation.push("Video")}>
|
||||||
<SessionCard
|
<VideoCard
|
||||||
playerName="Dean Machine"
|
playerName="Dean Machine"
|
||||||
location="Family Billiards, San Francisco"
|
location="Family Billiards, San Francisco"
|
||||||
gameType="Straight Pool"
|
gameType="Straight Pool"
|
||||||
@ -30,8 +30,8 @@ const SessionFeed: React.FC<SessionFeedProps> = ({ navigation }) => {
|
|||||||
medianRun="7.3"
|
medianRun="7.3"
|
||||||
duration="5:03:10"
|
duration="5:03:10"
|
||||||
shotPacing="0:00:26"
|
shotPacing="0:00:26"
|
||||||
imageURL={sampleSessionImage}
|
imageURL={sampleVideoImage}
|
||||||
sessionName="Dusting off the chalk"
|
videoName="Dusting off the chalk"
|
||||||
lastPlayed="Today at 2:37pm"
|
lastPlayed="Today at 2:37pm"
|
||||||
profileImageURL="https://www.pngall.com/wp-content/uploads/5/Profile-PNG-File.png"
|
profileImageURL="https://www.pngall.com/wp-content/uploads/5/Profile-PNG-File.png"
|
||||||
locationIconURL="https://www.shutterstock.com/image-vector/blank-map-marker-vector-illustration-260nw-1150566347.jpg"
|
locationIconURL="https://www.shutterstock.com/image-vector/blank-map-marker-vector-illustration-260nw-1150566347.jpg"
|
||||||
@ -49,4 +49,4 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default SessionFeed;
|
export default VideoFeed;
|
Loading…
Reference in New Issue
Block a user