diff --git a/.prettierrc b/.prettierrc index 544138b..3baced4 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,4 @@ { - "singleQuote": true + "singleQuote": true, + "endOfLine": "auto" } diff --git a/.vscode/settings.json b/.vscode/settings.json index 5d671d5..08ad711 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,6 @@ "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescriptreact]": { - "editor.defaultFormatter": "dbaeumer.vscode-eslint" + "editor.defaultFormatter": "esbenp.prettier-vscode" } } diff --git a/apps/mobile/.eslintrc.js b/apps/mobile/.eslintrc.js index e3da589..6681540 100644 --- a/apps/mobile/.eslintrc.js +++ b/apps/mobile/.eslintrc.js @@ -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'] }, diff --git a/apps/mobile/app/(tabs)/_layout.tsx b/apps/mobile/app/(tabs)/_layout.tsx index 1d235a1..b37f63d 100644 --- a/apps/mobile/app/(tabs)/_layout.tsx +++ b/apps/mobile/app/(tabs)/_layout.tsx @@ -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['name']; - color: string; -}) { - return ; -} +import TabBarIcon from '../../components/TabBarIcon'; +import { globalStyles } from '../../styles/global'; export default function TabLayout() { - const colorScheme = useColorScheme(); - return ( , - headerRight: () => ( - - - {({ pressed }) => ( - - )} - - + title: 'Home', + tabBarIcon: ({ focused }) => ( + ), }} /> , + title: 'About', + tabBarIcon: ({ focused }) => ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), }} /> diff --git a/apps/mobile/app/(tabs)/about.tsx b/apps/mobile/app/(tabs)/about.tsx new file mode 100644 index 0000000..fc530b8 --- /dev/null +++ b/apps/mobile/app/(tabs)/about.tsx @@ -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 ( + + + No content is served from movie-web directly and movie web does not host + anything. + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + }, + title: { + fontSize: 20, + fontWeight: 'bold', + }, + separator: { + marginVertical: 30, + height: 1, + width: '80%', + }, +}); diff --git a/apps/mobile/app/(tabs)/account.tsx b/apps/mobile/app/(tabs)/account.tsx new file mode 100644 index 0000000..e28c546 --- /dev/null +++ b/apps/mobile/app/(tabs)/account.tsx @@ -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 ( + + + Hey Bro! what are you up to? + + + ); +} diff --git a/apps/mobile/app/(tabs)/index.tsx b/apps/mobile/app/(tabs)/index.tsx index 3a5bb5f..58d9f6b 100644 --- a/apps/mobile/app/(tabs)/index.tsx +++ b/apps/mobile/app/(tabs)/index.tsx @@ -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 ( - - Tab One - - - + + + Movies will be listed here + + ); } - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - title: { - fontSize: 20, - fontWeight: 'bold', - }, - separator: { - marginVertical: 30, - height: 1, - width: '80%', - }, -}); diff --git a/apps/mobile/app/(tabs)/search/Searchbar.tsx b/apps/mobile/app/(tabs)/search/Searchbar.tsx new file mode 100644 index 0000000..73fd655 --- /dev/null +++ b/apps/mobile/app/(tabs)/search/Searchbar.tsx @@ -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(null); + + useFocusEffect( + useCallback(() => { + // When the screen is focused + const focus = () => { + setTimeout(() => { + inputRef?.current?.focus(); + }, 20); + }; + focus(); + return focus; // cleanup + }, []), + ); + + return ( + + + + + setKeyword(text)} + ref={inputRef} + placeholder="What are you looking for?" + placeholderTextColor={Colors.dark.shade200} + style={[ + globalStyles.input, + globalStyles.fOpenSansRegular, + { + width: '100%', + }, + ]} + > + + ); +} diff --git a/apps/mobile/app/(tabs)/search/_layout.tsx b/apps/mobile/app/(tabs)/search/_layout.tsx new file mode 100644 index 0000000..5fc46d9 --- /dev/null +++ b/apps/mobile/app/(tabs)/search/_layout.tsx @@ -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 ( + + + Search + + } + subtitle="Looking for something?" + > + + + + + + + + + + + + + + + ); +} diff --git a/apps/mobile/app/(tabs)/search/styles.tsx b/apps/mobile/app/(tabs)/search/styles.tsx new file mode 100644 index 0000000..d9ac5b0 --- /dev/null +++ b/apps/mobile/app/(tabs)/search/styles.tsx @@ -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; diff --git a/apps/mobile/app/(tabs)/settings.tsx b/apps/mobile/app/(tabs)/settings.tsx new file mode 100644 index 0000000..5dd243d --- /dev/null +++ b/apps/mobile/app/(tabs)/settings.tsx @@ -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 ( + + + Settings would be listed in here. Coming soon + + + ); +} diff --git a/apps/mobile/app/(tabs)/two.tsx b/apps/mobile/app/(tabs)/two.tsx deleted file mode 100644 index cc6a377..0000000 --- a/apps/mobile/app/(tabs)/two.tsx +++ /dev/null @@ -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 ( - - Tab Two - - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: 'center', - justifyContent: 'center', - }, - title: { - fontSize: 20, - fontWeight: 'bold', - }, - separator: { - marginVertical: 30, - height: 1, - width: '80%', - }, -}); diff --git a/apps/mobile/app/[...missing].tsx b/apps/mobile/app/[...missing].tsx index 6f991b4..78408c6 100644 --- a/apps/mobile/app/[...missing].tsx +++ b/apps/mobile/app/[...missing].tsx @@ -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 ( <> - This screen doesn't exist. + + This screen doesn't exist. + - Go to home screen! + Go to home screen! diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx index cdd4484..7428383 100644 --- a/apps/mobile/app/_layout.tsx +++ b/apps/mobile/app/_layout.tsx @@ -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 ( - + diff --git a/apps/mobile/app/modal.tsx b/apps/mobile/app/modal.tsx index 863ec18..2d22e9a 100644 --- a/apps/mobile/app/modal.tsx +++ b/apps/mobile/app/modal.tsx @@ -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 ( - Modal - - + Modal + + Modal?! {/* Use a light status bar on iOS to account for the black space above the modal */} diff --git a/apps/mobile/assets/fonts/OpenSans-Bold.ttf b/apps/mobile/assets/fonts/OpenSans-Bold.ttf new file mode 100644 index 0000000..98c74e0 Binary files /dev/null and b/apps/mobile/assets/fonts/OpenSans-Bold.ttf differ diff --git a/apps/mobile/assets/fonts/OpenSans-ExtraBold.ttf b/apps/mobile/assets/fonts/OpenSans-ExtraBold.ttf new file mode 100644 index 0000000..4eb3393 Binary files /dev/null and b/apps/mobile/assets/fonts/OpenSans-ExtraBold.ttf differ diff --git a/apps/mobile/assets/fonts/OpenSans-Light.ttf b/apps/mobile/assets/fonts/OpenSans-Light.ttf new file mode 100644 index 0000000..ea175cc Binary files /dev/null and b/apps/mobile/assets/fonts/OpenSans-Light.ttf differ diff --git a/apps/mobile/assets/fonts/OpenSans-Medium.ttf b/apps/mobile/assets/fonts/OpenSans-Medium.ttf new file mode 100644 index 0000000..ae71693 Binary files /dev/null and b/apps/mobile/assets/fonts/OpenSans-Medium.ttf differ diff --git a/apps/mobile/assets/fonts/OpenSans-Regular.ttf b/apps/mobile/assets/fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..67803bb Binary files /dev/null and b/apps/mobile/assets/fonts/OpenSans-Regular.ttf differ diff --git a/apps/mobile/assets/fonts/OpenSans-SemiBold.ttf b/apps/mobile/assets/fonts/OpenSans-SemiBold.ttf new file mode 100644 index 0000000..e5ab464 Binary files /dev/null and b/apps/mobile/assets/fonts/OpenSans-SemiBold.ttf differ diff --git a/apps/mobile/components/EditScreenInfo.tsx b/apps/mobile/components/EditScreenInfo.tsx deleted file mode 100644 index d1954ad..0000000 --- a/apps/mobile/components/EditScreenInfo.tsx +++ /dev/null @@ -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 ( - - - - Open up the code for this screen: - - - - {path} - - - - Change any of the text, save the file, and your app will automatically - update. - - - - - - - Tap here if your app doesn't automatically update after making - changes - - - - - ); -} - -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', - }, -}); diff --git a/apps/mobile/components/Styled.tsx b/apps/mobile/components/Styled.tsx new file mode 100644 index 0000000..8662625 --- /dev/null +++ b/apps/mobile/components/Styled.tsx @@ -0,0 +1,44 @@ +import { Text } from 'react-native'; + +export const RegularText = ({ style, children, ...rest }: Text['props']) => { + return ( + + {children} + + ); +}; +export const BoldText = ({ style, children, ...rest }: Text['props']) => { + return ( + + {children} + + ); +}; +export const SemiBoldText = ({ style, children, ...rest }: Text['props']) => { + return ( + + {children} + + ); +}; +export const MediumText = ({ style, children, ...rest }: Text['props']) => { + return ( + + {children} + + ); +}; +export const ExtraBoldText = ({ style, children, ...rest }: Text['props']) => { + return ( + + {children} + + ); +}; +export const LightText = ({ style, children, ...rest }: Text['props']) => { + return ( + + {children} + + ); +}; diff --git a/apps/mobile/components/StyledText.tsx b/apps/mobile/components/StyledText.tsx deleted file mode 100644 index aa3977c..0000000 --- a/apps/mobile/components/StyledText.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { Text, TextProps } from './Themed'; - -export function MonoText(props: TextProps) { - return ; -} diff --git a/apps/mobile/components/TabBarIcon.tsx b/apps/mobile/components/TabBarIcon.tsx new file mode 100644 index 0000000..b1ff95f --- /dev/null +++ b/apps/mobile/components/TabBarIcon.tsx @@ -0,0 +1,21 @@ +import { FontAwesome } from '@expo/vector-icons'; +import Colors from '../constants/Colors'; + +type Props = { + focused?: boolean; + color?: string; +} & React.ComponentProps; + +export default function TabBarIcon({ + color = Colors.dark.shade300, + focused, + ...rest +}: Props) { + return ( + + ); +} diff --git a/apps/mobile/components/Themed.tsx b/apps/mobile/components/Themed.tsx deleted file mode 100644 index 92818e6..0000000 --- a/apps/mobile/components/Themed.tsx +++ /dev/null @@ -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 ; -} - -export function View(props: ViewProps) { - const { style, lightColor, darkColor, ...otherProps } = props; - const backgroundColor = useThemeColor( - { light: lightColor, dark: darkColor }, - 'background', - ); - - return ; -} diff --git a/apps/mobile/components/item/item.tsx b/apps/mobile/components/item/item.tsx new file mode 100644 index 0000000..f413cac --- /dev/null +++ b/apps/mobile/components/item/item.tsx @@ -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 ( + + + + + Hamilton + + + Movie + + + + 2023 + + + + ); +} diff --git a/apps/mobile/components/item/styles.tsx b/apps/mobile/components/item/styles.tsx new file mode 100644 index 0000000..7ce5fc2 --- /dev/null +++ b/apps/mobile/components/item/styles.tsx @@ -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; diff --git a/apps/mobile/components/layout/screenLayout.tsx b/apps/mobile/components/layout/screenLayout.tsx new file mode 100644 index 0000000..b752aa2 --- /dev/null +++ b/apps/mobile/components/layout/screenLayout.tsx @@ -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 ( + + {typeof title === 'string' && ( + {title} + )} + {typeof title !== 'string' && title} + + {subtitle} + + {children} + + ); +} diff --git a/apps/mobile/components/layout/styles.tsx b/apps/mobile/components/layout/styles.tsx new file mode 100644 index 0000000..3fa7d38 --- /dev/null +++ b/apps/mobile/components/layout/styles.tsx @@ -0,0 +1,10 @@ +import { Dimensions, StyleSheet } from 'react-native'; + +export const styles = StyleSheet.create({ + container: { + minHeight: Dimensions.get('window').height, + }, + children: { + paddingVertical: 12, + }, +}); diff --git a/apps/mobile/constants/Colors.ts b/apps/mobile/constants/Colors.ts index 1c706c7..8e7bb23 100644 --- a/apps/mobile/constants/Colors.ts +++ b/apps/mobile/constants/Colors.ts @@ -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', }, }; diff --git a/apps/mobile/constants/General.ts b/apps/mobile/constants/General.ts new file mode 100644 index 0000000..7e981d6 --- /dev/null +++ b/apps/mobile/constants/General.ts @@ -0,0 +1 @@ +export const TMDB_POSTER_PATH = `https://image.tmdb.org/t/p`; diff --git a/apps/mobile/styles/global.tsx b/apps/mobile/styles/global.tsx new file mode 100644 index 0000000..d044512 --- /dev/null +++ b/apps/mobile/styles/global.tsx @@ -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, + }, +});