mirror of
https://github.com/movie-web/native-app.git
synced 2025-09-13 10:23:24 +00:00
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"singleQuote": true
|
||||
"singleQuote": true,
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -6,6 +6,6 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,12 @@ module.exports = {
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'react/require-default-props': 'off',
|
||||
'react/destructuring-assignment': 'off',
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
endOfLine: 'auto',
|
||||
},
|
||||
],
|
||||
'react/jsx-filename-extension': [
|
||||
'error',
|
||||
{ extensions: ['.js', '.tsx', '.jsx'] },
|
||||
|
@@ -1,54 +1,102 @@
|
||||
import FontAwesome from '@expo/vector-icons/FontAwesome';
|
||||
import { Link, Tabs } from 'expo-router';
|
||||
import { Pressable, useColorScheme } from 'react-native';
|
||||
import { Tabs } from 'expo-router';
|
||||
|
||||
import Colors from '../../constants/Colors';
|
||||
|
||||
/**
|
||||
* You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
|
||||
*/
|
||||
function TabBarIcon(props: {
|
||||
name: React.ComponentProps<typeof FontAwesome>['name'];
|
||||
color: string;
|
||||
}) {
|
||||
return <FontAwesome size={28} style={{ marginBottom: -3 }} {...props} />;
|
||||
}
|
||||
import TabBarIcon from '../../components/TabBarIcon';
|
||||
import { globalStyles } from '../../styles/global';
|
||||
|
||||
export default function TabLayout() {
|
||||
const colorScheme = useColorScheme();
|
||||
|
||||
return (
|
||||
<Tabs
|
||||
sceneContainerStyle={{
|
||||
backgroundColor: '#000',
|
||||
}}
|
||||
screenOptions={{
|
||||
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
||||
headerShown: false,
|
||||
tabBarActiveTintColor: Colors.dark.purple100,
|
||||
tabBarStyle: {
|
||||
backgroundColor: Colors.dark.shade700,
|
||||
borderTopColor: 'transparent',
|
||||
borderTopRightRadius: 20,
|
||||
borderTopLeftRadius: 20,
|
||||
height: 80,
|
||||
},
|
||||
tabBarItemStyle: {
|
||||
paddingVertical: 18,
|
||||
height: 82,
|
||||
},
|
||||
tabBarLabelStyle: [
|
||||
{
|
||||
marginTop: 2,
|
||||
},
|
||||
globalStyles.fOpenSansMedium,
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Tabs.Screen
|
||||
name="index"
|
||||
options={{
|
||||
title: 'Tab One',
|
||||
tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
|
||||
headerRight: () => (
|
||||
<Link href="/modal" asChild>
|
||||
<Pressable>
|
||||
{({ pressed }) => (
|
||||
<FontAwesome
|
||||
name="info-circle"
|
||||
size={25}
|
||||
color={Colors[colorScheme ?? 'light'].text}
|
||||
style={{ marginRight: 15, opacity: pressed ? 0.5 : 1 }}
|
||||
/>
|
||||
)}
|
||||
</Pressable>
|
||||
</Link>
|
||||
title: 'Home',
|
||||
tabBarIcon: ({ focused }) => (
|
||||
<TabBarIcon name="home" focused={focused} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="two"
|
||||
name="about"
|
||||
options={{
|
||||
title: 'Tab Two',
|
||||
tabBarIcon: ({ color }) => <TabBarIcon name="code" color={color} />,
|
||||
title: 'About',
|
||||
tabBarIcon: ({ focused }) => (
|
||||
<TabBarIcon name="info-circle" focused={focused} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="search"
|
||||
options={{
|
||||
title: 'Search',
|
||||
tabBarLabel: '',
|
||||
tabBarShowLabel: false,
|
||||
tabBarLabelStyle: {
|
||||
display: 'none',
|
||||
},
|
||||
tabBarIconStyle: {},
|
||||
tabBarIcon: () => (
|
||||
<TabBarIcon
|
||||
style={{
|
||||
position: 'relative',
|
||||
top: -1,
|
||||
backgroundColor: Colors.dark.purple400,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
aspectRatio: 1,
|
||||
borderRadius: 100,
|
||||
textAlign: 'center',
|
||||
textAlignVertical: 'center',
|
||||
height: 56,
|
||||
}}
|
||||
name="search"
|
||||
color="#FFF"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="settings"
|
||||
options={{
|
||||
title: 'Settings',
|
||||
tabBarIcon: ({ focused }) => (
|
||||
<TabBarIcon name="cog" focused={focused} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Tabs.Screen
|
||||
name="account"
|
||||
options={{
|
||||
title: 'Account',
|
||||
tabBarIcon: ({ focused }) => (
|
||||
<TabBarIcon name="user" focused={focused} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Tabs>
|
||||
|
36
apps/mobile/app/(tabs)/about.tsx
Normal file
36
apps/mobile/app/(tabs)/about.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import ScreenLayout from '../../components/layout/screenLayout';
|
||||
import { globalStyles } from '../../styles/global';
|
||||
import { RegularText } from '../../components/Styled';
|
||||
|
||||
export default function AboutScreen() {
|
||||
return (
|
||||
<ScreenLayout
|
||||
title="About"
|
||||
subtitle="What is movie-web and how content is served?"
|
||||
>
|
||||
<RegularText style={globalStyles.textWhite}>
|
||||
No content is served from movie-web directly and movie web does not host
|
||||
anything.
|
||||
</RegularText>
|
||||
</ScreenLayout>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
separator: {
|
||||
marginVertical: 30,
|
||||
height: 1,
|
||||
width: '80%',
|
||||
},
|
||||
});
|
18
apps/mobile/app/(tabs)/account.tsx
Normal file
18
apps/mobile/app/(tabs)/account.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
|
||||
import { globalStyles } from '../../styles/global';
|
||||
import ScreenLayout from '../../components/layout/screenLayout';
|
||||
import { RegularText } from '../../components/Styled';
|
||||
|
||||
export default function AccountScreen() {
|
||||
return (
|
||||
<ScreenLayout
|
||||
title="Account"
|
||||
subtitle="Manage your movie web account from here"
|
||||
>
|
||||
<RegularText style={globalStyles.textWhite}>
|
||||
Hey Bro! what are you up to?
|
||||
</RegularText>
|
||||
</ScreenLayout>
|
||||
);
|
||||
}
|
@@ -1,35 +1,13 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import { RegularText } from '../../components/Styled';
|
||||
import ScreenLayout from '../../components/layout/screenLayout';
|
||||
import { globalStyles } from '../../styles/global';
|
||||
|
||||
import EditScreenInfo from '../../components/EditScreenInfo';
|
||||
import { Text, View } from '../../components/Themed';
|
||||
|
||||
export default function TabOneScreen() {
|
||||
export default function HomeScreen() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Tab One</Text>
|
||||
<View
|
||||
style={styles.separator}
|
||||
lightColor="#eee"
|
||||
darkColor="rgba(255,255,255,0.1)"
|
||||
/>
|
||||
<EditScreenInfo path="app/(tabs)/index.tsx" />
|
||||
</View>
|
||||
<ScreenLayout title="Home" subtitle="This is where all magic happens">
|
||||
<RegularText style={globalStyles.textWhite}>
|
||||
Movies will be listed here
|
||||
</RegularText>
|
||||
</ScreenLayout>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
separator: {
|
||||
marginVertical: 30,
|
||||
height: 1,
|
||||
width: '80%',
|
||||
},
|
||||
});
|
||||
|
64
apps/mobile/app/(tabs)/search/Searchbar.tsx
Normal file
64
apps/mobile/app/(tabs)/search/Searchbar.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { FontAwesome5 } from '@expo/vector-icons';
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { TextInput } from 'react-native-gesture-handler';
|
||||
import { globalStyles } from '../../../styles/global';
|
||||
import Colors from '../../../constants/Colors';
|
||||
import { useFocusEffect } from 'expo-router';
|
||||
|
||||
export default function Searchbar() {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
// When the screen is focused
|
||||
const focus = () => {
|
||||
setTimeout(() => {
|
||||
inputRef?.current?.focus();
|
||||
}, 20);
|
||||
};
|
||||
focus();
|
||||
return focus; // cleanup
|
||||
}, []),
|
||||
);
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
...globalStyles.flexRow,
|
||||
...globalStyles.itemsCenter,
|
||||
...globalStyles.border,
|
||||
...globalStyles.roundedFull,
|
||||
marginTop: 14,
|
||||
marginBottom: 24,
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
...globalStyles.justifyCenter,
|
||||
...globalStyles.itemsCenter,
|
||||
width: 48,
|
||||
marginLeft: 4,
|
||||
}}
|
||||
>
|
||||
<FontAwesome5 name="search" size={18} color={Colors.dark.shade200} />
|
||||
</View>
|
||||
<TextInput
|
||||
value={keyword}
|
||||
autoFocus={true}
|
||||
onChangeText={(text) => setKeyword(text)}
|
||||
ref={inputRef}
|
||||
placeholder="What are you looking for?"
|
||||
placeholderTextColor={Colors.dark.shade200}
|
||||
style={[
|
||||
globalStyles.input,
|
||||
globalStyles.fOpenSansRegular,
|
||||
{
|
||||
width: '100%',
|
||||
},
|
||||
]}
|
||||
></TextInput>
|
||||
</View>
|
||||
);
|
||||
}
|
41
apps/mobile/app/(tabs)/search/_layout.tsx
Normal file
41
apps/mobile/app/(tabs)/search/_layout.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Dimensions, ScrollView, View } from 'react-native';
|
||||
|
||||
import { globalStyles } from '../../../styles/global';
|
||||
import ScreenLayout from '../../../components/layout/screenLayout';
|
||||
import { TextInput } from 'react-native-gesture-handler';
|
||||
import styles from './styles';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useFocusEffect } from 'expo-router';
|
||||
import { BoldText } from '../../../components/Styled';
|
||||
import Item from '../../../components/item/item';
|
||||
import Searchbar from './Searchbar';
|
||||
|
||||
export default function SearchScreen() {
|
||||
return (
|
||||
<ScrollView>
|
||||
<ScreenLayout
|
||||
title={
|
||||
<View
|
||||
style={{ ...globalStyles.flexRow, ...globalStyles.itemsCenter }}
|
||||
>
|
||||
<BoldText style={globalStyles.sectionTitle}>Search</BoldText>
|
||||
</View>
|
||||
}
|
||||
subtitle="Looking for something?"
|
||||
>
|
||||
<Searchbar />
|
||||
<View style={styles.items}>
|
||||
<View style={styles.itemOuter}>
|
||||
<Item />
|
||||
</View>
|
||||
<View style={styles.itemOuter}>
|
||||
<Item />
|
||||
</View>
|
||||
<View style={styles.itemOuter}>
|
||||
<Item />
|
||||
</View>
|
||||
</View>
|
||||
</ScreenLayout>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
19
apps/mobile/app/(tabs)/search/styles.tsx
Normal file
19
apps/mobile/app/(tabs)/search/styles.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
items: {
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'flex-start',
|
||||
width: '100%',
|
||||
flexDirection: 'row',
|
||||
flex: 1,
|
||||
},
|
||||
itemOuter: {
|
||||
flexBasis: '50%',
|
||||
paddingHorizontal: 12,
|
||||
paddingBottom: 12,
|
||||
},
|
||||
});
|
||||
|
||||
export default styles;
|
14
apps/mobile/app/(tabs)/settings.tsx
Normal file
14
apps/mobile/app/(tabs)/settings.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { RegularText } from '../../components/Styled';
|
||||
import ScreenLayout from '../../components/layout/screenLayout';
|
||||
import { globalStyles } from '../../styles/global';
|
||||
import { StyleSheet, Text } from 'react-native';
|
||||
|
||||
export default function SettingsScreen() {
|
||||
return (
|
||||
<ScreenLayout title="Settings" subtitle="Need to change something?">
|
||||
<RegularText style={globalStyles.textWhite}>
|
||||
Settings would be listed in here. Coming soon
|
||||
</RegularText>
|
||||
</ScreenLayout>
|
||||
);
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import EditScreenInfo from '../../components/EditScreenInfo';
|
||||
import { Text, View } from '../../components/Themed';
|
||||
|
||||
export default function TabTwoScreen() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Tab Two</Text>
|
||||
<View
|
||||
style={styles.separator}
|
||||
lightColor="#eee"
|
||||
darkColor="rgba(255,255,255,0.1)"
|
||||
/>
|
||||
<EditScreenInfo path="app/(tabs)/two.tsx" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
title: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
separator: {
|
||||
marginVertical: 30,
|
||||
height: 1,
|
||||
width: '80%',
|
||||
},
|
||||
});
|
@@ -1,17 +1,18 @@
|
||||
import { Link, Stack } from 'expo-router';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { Text, View } from '../components/Themed';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
import { BoldText, RegularText } from '../components/Styled';
|
||||
|
||||
export default function NotFoundScreen() {
|
||||
return (
|
||||
<>
|
||||
<Stack.Screen options={{ title: 'Oops!' }} />
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>This screen doesn't exist.</Text>
|
||||
<BoldText style={styles.title}>
|
||||
This screen doesn't exist.
|
||||
</BoldText>
|
||||
|
||||
<Link href="/" style={styles.link}>
|
||||
<Text style={styles.linkText}>Go to home screen!</Text>
|
||||
<RegularText style={styles.linkText}>Go to home screen!</RegularText>
|
||||
</Link>
|
||||
</View>
|
||||
</>
|
||||
|
@@ -9,6 +9,8 @@ import { SplashScreen, Stack } from 'expo-router';
|
||||
import { useEffect } from 'react';
|
||||
import { useColorScheme } from 'react-native';
|
||||
|
||||
import Colors from '../constants/Colors';
|
||||
|
||||
export {
|
||||
// Catch any errors thrown by the Layout component.
|
||||
ErrorBoundary,
|
||||
@@ -26,7 +28,12 @@ SplashScreen.preventAutoHideAsync();
|
||||
export default function RootLayout() {
|
||||
const [loaded, error] = useFonts({
|
||||
// eslint-disable-next-line global-require
|
||||
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
|
||||
OpenSansRegular: require('../assets/fonts/OpenSans-Regular.ttf'),
|
||||
OpenSansLight: require('../assets/fonts/OpenSans-Light.ttf'),
|
||||
OpenSansMedium: require('../assets/fonts/OpenSans-Medium.ttf'),
|
||||
OpenSansBold: require('../assets/fonts/OpenSans-Bold.ttf'),
|
||||
OpenSansSemiBold: require('../assets/fonts/OpenSans-SemiBold.ttf'),
|
||||
OpenSansExtra: require('../assets/fonts/OpenSans-ExtraBold.ttf'),
|
||||
...FontAwesome.font,
|
||||
});
|
||||
|
||||
@@ -53,7 +60,15 @@ function RootLayoutNav() {
|
||||
|
||||
return (
|
||||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
||||
<Stack>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
gestureEnabled: true,
|
||||
headerShown: false,
|
||||
contentStyle: {
|
||||
backgroundColor: Colors.dark.shade900,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="modal" options={{ presentation: 'modal' }} />
|
||||
</Stack>
|
||||
|
@@ -1,19 +1,13 @@
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
import { Platform, StyleSheet } from 'react-native';
|
||||
|
||||
import EditScreenInfo from '../components/EditScreenInfo';
|
||||
import { Text, View } from '../components/Themed';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
import { BoldText, RegularText } from '../components/Styled';
|
||||
|
||||
export default function ModalScreen() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={styles.title}>Modal</Text>
|
||||
<View
|
||||
style={styles.separator}
|
||||
lightColor="#eee"
|
||||
darkColor="rgba(255,255,255,0.1)"
|
||||
/>
|
||||
<EditScreenInfo path="app/modal.tsx" />
|
||||
<BoldText style={styles.title}>Modal</BoldText>
|
||||
<View style={styles.separator} />
|
||||
<RegularText>Modal?!</RegularText>
|
||||
|
||||
{/* Use a light status bar on iOS to account for the black space above the modal */}
|
||||
<StatusBar style={Platform.OS === 'ios' ? 'light' : 'auto'} />
|
||||
|
BIN
apps/mobile/assets/fonts/OpenSans-Bold.ttf
Normal file
BIN
apps/mobile/assets/fonts/OpenSans-Bold.ttf
Normal file
Binary file not shown.
BIN
apps/mobile/assets/fonts/OpenSans-ExtraBold.ttf
Normal file
BIN
apps/mobile/assets/fonts/OpenSans-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
apps/mobile/assets/fonts/OpenSans-Light.ttf
Normal file
BIN
apps/mobile/assets/fonts/OpenSans-Light.ttf
Normal file
Binary file not shown.
BIN
apps/mobile/assets/fonts/OpenSans-Medium.ttf
Normal file
BIN
apps/mobile/assets/fonts/OpenSans-Medium.ttf
Normal file
Binary file not shown.
BIN
apps/mobile/assets/fonts/OpenSans-Regular.ttf
Normal file
BIN
apps/mobile/assets/fonts/OpenSans-Regular.ttf
Normal file
Binary file not shown.
BIN
apps/mobile/assets/fonts/OpenSans-SemiBold.ttf
Normal file
BIN
apps/mobile/assets/fonts/OpenSans-SemiBold.ttf
Normal file
Binary file not shown.
@@ -1,82 +0,0 @@
|
||||
import React from 'react';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { ExternalLink } from './ExternalLink';
|
||||
import { MonoText } from './StyledText';
|
||||
import { Text, View } from './Themed';
|
||||
import Colors from '../constants/Colors';
|
||||
|
||||
export default function EditScreenInfo({ path }: { path: string }) {
|
||||
return (
|
||||
<View>
|
||||
<View style={styles.getStartedContainer}>
|
||||
<Text
|
||||
style={styles.getStartedText}
|
||||
lightColor="rgba(0,0,0,0.8)"
|
||||
darkColor="rgba(255,255,255,0.8)"
|
||||
>
|
||||
Open up the code for this screen:
|
||||
</Text>
|
||||
|
||||
<View
|
||||
style={[styles.codeHighlightContainer, styles.homeScreenFilename]}
|
||||
darkColor="rgba(255,255,255,0.05)"
|
||||
lightColor="rgba(0,0,0,0.05)"
|
||||
>
|
||||
<MonoText>{path}</MonoText>
|
||||
</View>
|
||||
|
||||
<Text
|
||||
style={styles.getStartedText}
|
||||
lightColor="rgba(0,0,0,0.8)"
|
||||
darkColor="rgba(255,255,255,0.8)"
|
||||
>
|
||||
Change any of the text, save the file, and your app will automatically
|
||||
update.
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.helpContainer}>
|
||||
<ExternalLink
|
||||
style={styles.helpLink}
|
||||
href="https://docs.expo.io/get-started/create-a-new-app/#opening-the-app-on-your-phonetablet"
|
||||
>
|
||||
<Text style={styles.helpLinkText} lightColor={Colors.light.tint}>
|
||||
Tap here if your app doesn't automatically update after making
|
||||
changes
|
||||
</Text>
|
||||
</ExternalLink>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
getStartedContainer: {
|
||||
alignItems: 'center',
|
||||
marginHorizontal: 50,
|
||||
},
|
||||
homeScreenFilename: {
|
||||
marginVertical: 7,
|
||||
},
|
||||
codeHighlightContainer: {
|
||||
borderRadius: 3,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
getStartedText: {
|
||||
fontSize: 17,
|
||||
lineHeight: 24,
|
||||
textAlign: 'center',
|
||||
},
|
||||
helpContainer: {
|
||||
marginTop: 15,
|
||||
marginHorizontal: 20,
|
||||
alignItems: 'center',
|
||||
},
|
||||
helpLink: {
|
||||
paddingVertical: 15,
|
||||
},
|
||||
helpLinkText: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
});
|
44
apps/mobile/components/Styled.tsx
Normal file
44
apps/mobile/components/Styled.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { Text } from 'react-native';
|
||||
|
||||
export const RegularText = ({ style, children, ...rest }: Text['props']) => {
|
||||
return (
|
||||
<Text style={[{ fontFamily: 'OpenSansRegular' }, style]} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
export const BoldText = ({ style, children, ...rest }: Text['props']) => {
|
||||
return (
|
||||
<Text style={[{ fontFamily: 'OpenSansBold' }, style]} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
export const SemiBoldText = ({ style, children, ...rest }: Text['props']) => {
|
||||
return (
|
||||
<Text style={[{ fontFamily: 'OpenSansSemiBold' }, style]} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
export const MediumText = ({ style, children, ...rest }: Text['props']) => {
|
||||
return (
|
||||
<Text style={[{ fontFamily: 'OpenSansMedium' }, style]} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
export const ExtraBoldText = ({ style, children, ...rest }: Text['props']) => {
|
||||
return (
|
||||
<Text style={[{ fontFamily: 'OpenSansExtraBold' }, style]} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
export const LightText = ({ style, children, ...rest }: Text['props']) => {
|
||||
return (
|
||||
<Text style={[{ fontFamily: 'OpenSansLight' }, style]} {...rest}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
@@ -1,5 +0,0 @@
|
||||
import { Text, TextProps } from './Themed';
|
||||
|
||||
export function MonoText(props: TextProps) {
|
||||
return <Text {...props} style={[props.style, { fontFamily: 'SpaceMono' }]} />;
|
||||
}
|
21
apps/mobile/components/TabBarIcon.tsx
Normal file
21
apps/mobile/components/TabBarIcon.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { FontAwesome } from '@expo/vector-icons';
|
||||
import Colors from '../constants/Colors';
|
||||
|
||||
type Props = {
|
||||
focused?: boolean;
|
||||
color?: string;
|
||||
} & React.ComponentProps<typeof FontAwesome>;
|
||||
|
||||
export default function TabBarIcon({
|
||||
color = Colors.dark.shade300,
|
||||
focused,
|
||||
...rest
|
||||
}: Props) {
|
||||
return (
|
||||
<FontAwesome
|
||||
color={color || (focused ? Colors.dark.purple300 : Colors.dark.shade300)}
|
||||
size={24}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* Learn more about Light and Dark modes:
|
||||
* https://docs.expo.io/guides/color-schemes/
|
||||
*/
|
||||
|
||||
import {
|
||||
Text as DefaultText,
|
||||
View as DefaultView,
|
||||
useColorScheme,
|
||||
} from 'react-native';
|
||||
|
||||
import Colors from '../constants/Colors';
|
||||
|
||||
type ThemeProps = {
|
||||
lightColor?: string;
|
||||
darkColor?: string;
|
||||
};
|
||||
|
||||
export type TextProps = ThemeProps & DefaultText['props'];
|
||||
export type ViewProps = ThemeProps & DefaultView['props'];
|
||||
|
||||
export function useThemeColor(
|
||||
props: { light?: string; dark?: string },
|
||||
colorName: keyof typeof Colors.light & keyof typeof Colors.dark,
|
||||
) {
|
||||
const theme = useColorScheme() ?? 'light';
|
||||
const colorFromProps = props[theme];
|
||||
|
||||
if (colorFromProps) {
|
||||
return colorFromProps;
|
||||
}
|
||||
return Colors[theme][colorName];
|
||||
}
|
||||
|
||||
export function Text(props: TextProps) {
|
||||
const { style, lightColor, darkColor, ...otherProps } = props;
|
||||
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
|
||||
|
||||
return <DefaultText style={[{ color }, style]} {...otherProps} />;
|
||||
}
|
||||
|
||||
export function View(props: ViewProps) {
|
||||
const { style, lightColor, darkColor, ...otherProps } = props;
|
||||
const backgroundColor = useThemeColor(
|
||||
{ light: lightColor, dark: darkColor },
|
||||
'background',
|
||||
);
|
||||
|
||||
return <DefaultView style={[{ backgroundColor }, style]} {...otherProps} />;
|
||||
}
|
31
apps/mobile/components/item/item.tsx
Normal file
31
apps/mobile/components/item/item.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { globalStyles } from '../../styles/global';
|
||||
import { Image, Text, View } from 'react-native';
|
||||
import styles from './styles';
|
||||
import { BoldText, RegularText } from '../Styled';
|
||||
import { TMDB_POSTER_PATH } from '../../constants/General';
|
||||
|
||||
export default function Item() {
|
||||
return (
|
||||
<View style={styles.wrapper}>
|
||||
<View style={styles.imageWrapper}>
|
||||
<Image
|
||||
source={{
|
||||
uri: `${TMDB_POSTER_PATH}/w342//gdIrmf2DdY5mgN6ycVP0XlzKzbE.jpg`,
|
||||
width: 200,
|
||||
}}
|
||||
style={styles.image}
|
||||
/>
|
||||
</View>
|
||||
<BoldText style={globalStyles.textWhite}>Hamilton</BoldText>
|
||||
<View style={styles.meta}>
|
||||
<RegularText style={[globalStyles.textMuted, styles.smallText]}>
|
||||
Movie
|
||||
</RegularText>
|
||||
<View style={[globalStyles.dotSeperator]}></View>
|
||||
<RegularText style={[globalStyles.textMuted, styles.smallText]}>
|
||||
2023
|
||||
</RegularText>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
31
apps/mobile/components/item/styles.tsx
Normal file
31
apps/mobile/components/item/styles.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import Colors from '../../constants/Colors';
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
wrapper: {
|
||||
width: '100%',
|
||||
},
|
||||
imageWrapper: {
|
||||
borderRadius: 16,
|
||||
aspectRatio: 9 / 14,
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
marginBottom: 6,
|
||||
},
|
||||
image: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
resizeMode: 'cover',
|
||||
},
|
||||
meta: {
|
||||
flexDirection: 'row',
|
||||
gap: 6,
|
||||
alignItems: 'center',
|
||||
},
|
||||
smallText: {
|
||||
fontSize: 12,
|
||||
},
|
||||
});
|
||||
|
||||
export default styles;
|
31
apps/mobile/components/layout/screenLayout.tsx
Normal file
31
apps/mobile/components/layout/screenLayout.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { View } from 'react-native';
|
||||
import { globalStyles } from '../../styles/global';
|
||||
import { styles } from './styles';
|
||||
import { BoldText, RegularText } from '../Styled';
|
||||
|
||||
type Props = {
|
||||
title?: React.ReactNode | string;
|
||||
subtitle?: string;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export default function ScreenLayout({ title, subtitle, children }: Props) {
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
globalStyles.pageContainer,
|
||||
globalStyles.container,
|
||||
styles.container,
|
||||
]}
|
||||
>
|
||||
{typeof title === 'string' && (
|
||||
<BoldText style={globalStyles.sectionTitle}>{title}</BoldText>
|
||||
)}
|
||||
{typeof title !== 'string' && title}
|
||||
<RegularText style={[{ marginTop: 4 }, globalStyles.sectionSubtitle]}>
|
||||
{subtitle}
|
||||
</RegularText>
|
||||
<View style={styles.children}>{children}</View>
|
||||
</View>
|
||||
);
|
||||
}
|
10
apps/mobile/components/layout/styles.tsx
Normal file
10
apps/mobile/components/layout/styles.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Dimensions, StyleSheet } from 'react-native';
|
||||
|
||||
export const styles = StyleSheet.create({
|
||||
container: {
|
||||
minHeight: Dimensions.get('window').height,
|
||||
},
|
||||
children: {
|
||||
paddingVertical: 12,
|
||||
},
|
||||
});
|
@@ -15,5 +15,13 @@ export default {
|
||||
tint: tintColorDark,
|
||||
tabIconDefault: '#ccc',
|
||||
tabIconSelected: tintColorDark,
|
||||
purple100: '#C082FF',
|
||||
purple300: '#8D44D6',
|
||||
purple400: '#7831BF',
|
||||
shade50: '#676790',
|
||||
shade200: '#3F3F60',
|
||||
shade300: '#32324F',
|
||||
shade700: '#131322',
|
||||
shade900: '#0A0A12',
|
||||
},
|
||||
};
|
||||
|
1
apps/mobile/constants/General.ts
Normal file
1
apps/mobile/constants/General.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const TMDB_POSTER_PATH = `https://image.tmdb.org/t/p`;
|
75
apps/mobile/styles/global.tsx
Normal file
75
apps/mobile/styles/global.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
import Colors from '../constants/Colors';
|
||||
|
||||
export const globalStyles = StyleSheet.create({
|
||||
pageContainer: {
|
||||
flex: 1,
|
||||
backgroundColor: Colors.dark.shade900,
|
||||
paddingVertical: 48,
|
||||
},
|
||||
container: {
|
||||
padding: 24,
|
||||
},
|
||||
sectionTitle: {
|
||||
color: '#FFF',
|
||||
letterSpacing: -1.5,
|
||||
fontWeight: 'bold',
|
||||
fontSize: 28,
|
||||
},
|
||||
sectionSubtitle: {
|
||||
color: Colors.dark.shade200,
|
||||
fontSize: 14,
|
||||
},
|
||||
textWhite: {
|
||||
color: '#FFF',
|
||||
},
|
||||
flexRow: {
|
||||
flexDirection: 'row',
|
||||
},
|
||||
itemsCenter: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
justifyCenter: {
|
||||
justifyContent: 'center',
|
||||
},
|
||||
input: {
|
||||
borderRadius: 24,
|
||||
paddingRight: 18,
|
||||
paddingVertical: 12,
|
||||
color: '#FFF',
|
||||
},
|
||||
border: {
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(255,255,255,.1)',
|
||||
},
|
||||
roundedFull: {
|
||||
borderRadius: 34,
|
||||
},
|
||||
fOpenSansLight: {
|
||||
fontFamily: 'OpenSansLight',
|
||||
},
|
||||
fOpenSansBold: {
|
||||
fontFamily: 'OpenSansBold',
|
||||
},
|
||||
fOpenSansSemiBold: {
|
||||
fontFamily: 'OpenSansSemiBold',
|
||||
},
|
||||
fOpenSansMedium: {
|
||||
fontFamily: 'OpenSansMedium',
|
||||
},
|
||||
fOpenSansExtraBold: {
|
||||
fontFamily: 'OpenSansExtraBold',
|
||||
},
|
||||
fOpenSansRegular: {
|
||||
fontFamily: 'OpenSansRegular',
|
||||
},
|
||||
textMuted: {
|
||||
color: '#5F5F7A',
|
||||
},
|
||||
dotSeperator: {
|
||||
width: 4,
|
||||
height: 4,
|
||||
backgroundColor: '#5F5F7A',
|
||||
borderRadius: 50,
|
||||
},
|
||||
});
|
Reference in New Issue
Block a user