Part 2: Final Code
Next Steps
Now that you've built your basic video app, here are some ways to continue your Vega learning journey:
-
Join our forums: Connect with other Vega developers, share your experiences, and get help with your projects in our developer community.
-
Check out other Sample Apps: Explore our collection of sample applications.
Final Code
Here is the final code for the lab:
Folder Structure
src/
├── components/
| ├── index.ts
│ ├── Button.tsx
│ ├── Header.tsx
│ └── VideoCard.tsx
├── screens
| ├── index.ts
│ ├── LandingScreen.tsx
| ├── VideoDetailScreen.tsx
│ └── VideoPlaybackScreen.tsx
└── App.tsx
Components
index.ts
import Header from './Header';
import VideoCard from './VideoCard';
import Button from './Button';
export {
Header,
VideoCard,
Button,
}
Header.tsx
import * as React from 'react';
import {StyleSheet, Text, View} from 'react-native';
const Header = () => {
return (
<View style={styles.headerContainer}>
<Text style={styles.headerText}>Welcome to Vega!</Text>
</View>
);
};
const styles = StyleSheet.create({
headerContainer: {
padding: 10,
alignItems: 'center',
backgroundColor: '#232F3E',
width: '100%',
},
headerText: {
fontSize: 50,
fontWeight: '700',
color: '#FF9900',
},
});
export default Header;
VideoCard.tsx
import React, {useState} from 'react';
import {StyleSheet, Text, TouchableOpacity, Image, View} from 'react-native';
interface IProps {
title: string;
imgURL: string;
description: string;
pressFunction: Function;
}
const VideoCard = ({title, imgURL, description, pressFunction}: IProps) => {
const [focused, setFocused] = useState(false);
return (
<TouchableOpacity
style={[
styles.videoCardContainer,
focused ? styles.focused : styles.unfocused,
]}
onPress={() => pressFunction()}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}>
<Image style={styles.videoImage} source={{uri: imgURL}} />
<View style={styles.videoTextContainer}>
<Text style={styles.videoTitle}>{title}</Text>
<Text style={styles.videoDescription}>{description}</Text>
</View>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
videoCardContainer: {
height: 400,
width: 550,
margin: 10,
borderRadius: 5,
},
unfocused: {
borderWidth: 1,
borderColor: 'white',
},
focused: {
borderWidth: 5,
borderColor: 'yellow',
},
videoTextContainer: {
height: '25%',
display: 'flex',
justifyContent: 'space-around',
padding: 10,
},
videoImage: {
height: '75%',
},
videoTitle: {
fontSize: 20,
fontWeight: 'bold',
color: 'white',
},
videoDescription: {
color: 'white',
},
});
export default VideoCard;
Button.tsx
Screens
index.ts
import LandingScreen from './LandingScreen';
import VideoPlaybackScreen from './VideoPlaybackScreen';
import VideoDetailScreen from './VideoDetailScreen';
export {
LandingScreen,
VideoPlaybackScreen,
VideoDetailScreen
}
LandingScreen.tsx
import React, {useState, useEffect} from 'react';
import {FlatList, StyleSheet, View, Text} from 'react-native';
import {Header, VideoCard} from '../components';
import {TVFocusGuideView} from '@amazon-devices/react-native-kepler';
interface IVideo {
id: string;
title: string;
description: string;
duration: number;
thumbURL: string;
imgURL: string;
videoURL: string;
categories: Array<string>;
channel_id: number;
}
const LandingScreen = ({navigation}: any) => {
const [islandVideos, setIslandVideos] = useState<IVideo[]>([]);
const [underwaterVideos, setUnderwaterVideos] = useState<IVideo[]>([]);
const url = 'https://d2ob7xfxpe6plv.cloudfront.net/TestData.json';
const getAllVideos = () => {
fetch(url)
.then((response) => response.json())
.then((data) => {
// Filter videos for each category
const islands = data.testData.filter(
(video: IVideo) =>
video.categories && video.categories.includes('Costa Rica Islands'),
);
const underwater = data.testData.filter(
(video: IVideo) =>
video.categories &&
video.categories.includes('Costa Rica Underwater'),
);
setIslandVideos(islands);
setUnderwaterVideos(underwater);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
getAllVideos();
}, []);
return (
<>
<Header />
<TVFocusGuideView autoFocus={true}>
<Text style={styles.categoryTitle}>Costa Rica Islands</Text>
<FlatList
style={styles.flatList}
horizontal
data={islandVideos}
renderItem={({item}) => (
<View style={styles.itemContainer}>
<VideoCard
title={item.title}
description={
item.description.split(' ').slice(0, 20).join(' ') + '...'
}
imgURL={item.imgURL}
pressFunction={() =>
navigation.navigate('VideoDetailScreen', {video: item})
}
/>
</View>
)}
/>
</TVFocusGuideView>
<TVFocusGuideView autoFocus={true}>
<Text style={styles.categoryTitle}>Costa Rica Underwater</Text>
<FlatList
style={styles.flatList}
horizontal
data={underwaterVideos}
renderItem={({item}) => (
<View style={styles.itemContainer}>
<VideoCard
title={item.title}
description={
item.description.split(' ').slice(0, 20).join(' ') + '...'
}
imgURL={item.imgURL}
pressFunction={() =>
navigation.navigate('VideoDetailScreen', {video: item})
}
/>
</View>
)}
/>
</TVFocusGuideView>
</>
);
};
const styles = StyleSheet.create({
flatList: {
padding: 10,
},
itemContainer: {
margin: 10,
},
categoryTitle: {
fontSize: 24,
fontWeight: 'bold',
color: 'white',
marginLeft: 30,
},
});
export default LandingScreen;
VideoDetailScreen.tsx
import React from 'react';
import {ImageBackground, Text, View, StyleSheet} from 'react-native';
import {Button} from '../components';
const VideoDetailScreen = ({navigation, route}: any) => {
const video = route.params.video;
return (
<ImageBackground
source={{uri: video.imgURL}}
imageStyle={styles.image}
style={styles.screenContainer}>
<Text style={styles.videoTitle}>{video.title}</Text>
<Text style={styles.videoDescription}>{video.description}</Text>
<View style={styles.buttonContainer}>
<Button
buttonText="Watch Now"
pressFunction={() =>
navigation.navigate('VideoPlaybackScreen', {
videoURL: video.videoURL,
})
}
/>
<Button buttonText="Back" pressFunction={() => navigation.goBack()} />
</View>
</ImageBackground>
);
};
const styles = StyleSheet.create({
image: {
opacity: 0.2,
},
screenContainer: {
display: 'flex',
height: '100%',
justifyContent: 'center',
},
videoTitle: {
fontSize: 50,
color: 'white',
fontWeight: '700',
margin: 30,
},
videoDescription: {
color: 'white',
fontSize: 30,
margin: 30,
},
buttonContainer: {
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
marginLeft: 30,
width: '18%',
},
});
export default VideoDetailScreen;
VideoPlaybackScreen.tsx
import React from 'react';
import {View, StyleSheet, Text} from 'react-native';
import {Button} from '../components';
const VideoPlaybackScreen = ({navigation, route}: any) => {
return (
<View style={styles.playerContainer}>
<Text style={styles.playerPlaceholder}>{route.params.videoURL}</Text>
<View style={styles.buttonContainer}>
<Button buttonText="Back" pressFunction={() => navigation.goBack()} />
</View>
</View>
);
};
const styles = StyleSheet.create({
playerContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
buttonContainer: {
width: 100,
position: 'absolute',
top: 10,
left: 10,
},
playerPlaceholder: {
color: 'white',
fontSize: 30,
},
});
export default VideoPlaybackScreen;
App.tsx
import * as React from 'react';
import { NavigationContainer, DefaultTheme } from '@amazon-devices/react-navigation__native';
import { createStackNavigator } from '@amazon-devices/react-navigation__stack';
import { LandingScreen, VideoDetailScreen, VideoPlaybackScreen } from './screens';
export const App = () => {
const Stack = createStackNavigator();
const AppTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: "#232F3E",
text: "white",
},
};
return (
<NavigationContainer theme={AppTheme}>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name='LandingScreen' component={LandingScreen}/>
<Stack.Screen name='VideoDetailScreen' component={VideoDetailScreen}/>
<Stack.Screen name='VideoPlaybackScreen' component={VideoPlaybackScreen}/>
</Stack.Navigator>
</NavigationContainer>
);
}