commit 3025fbec9ca634e99bfd91d16aa14902da3e2bf2 Author: florian Date: Tue Sep 30 15:29:12 2025 +0200 Initial draft diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8c6c2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +.kotlin/ +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo + +app-example + +# generated native folders +/ios +/android diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..b7ed837 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1 @@ +{ "recommendations": ["expo.vscode-expo-tools"] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e2798e4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit", + "source.sortMembers": "explicit" + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..48dd63f --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Welcome to your Expo app 👋 + +This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). + +## Get started + +1. Install dependencies + + ```bash + npm install + ``` + +2. Start the app + + ```bash + npx expo start + ``` + +In the output, you'll find options to open the app in a + +- [development build](https://docs.expo.dev/develop/development-builds/introduction/) +- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) +- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) +- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo + +You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). + +## Get a fresh project + +When you're ready, run: + +```bash +npm run reset-project +``` + +This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. + +## Learn more + +To learn more about developing your project with Expo, look at the following resources: + +- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). +- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. + +## Join the community + +Join our community of developers creating universal apps. + +- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. +- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. diff --git a/app.json b/app.json new file mode 100644 index 0000000..c578a91 --- /dev/null +++ b/app.json @@ -0,0 +1,48 @@ +{ + "expo": { + "name": "my-drawer-app", + "slug": "my-drawer-app", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "mydrawerapp", + "userInterfaceStyle": "automatic", + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "backgroundColor": "#E6F4FE", + "foregroundImage": "./assets/images/android-icon-foreground.png", + "backgroundImage": "./assets/images/android-icon-background.png", + "monochromeImage": "./assets/images/android-icon-monochrome.png" + }, + "edgeToEdgeEnabled": true, + "predictiveBackGestureEnabled": false + }, + "web": { + "output": "static", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + "expo-router", + [ + "expo-splash-screen", + { + "image": "./assets/images/splash-icon.png", + "imageWidth": 200, + "resizeMode": "contain", + "backgroundColor": "#ffffff", + "dark": { + "backgroundColor": "#000000" + } + } + ] + ], + "experiments": { + "typedRoutes": true, + "reactCompiler": true + } + } +} diff --git a/app/HomeScreen.tsx b/app/HomeScreen.tsx new file mode 100644 index 0000000..f392233 --- /dev/null +++ b/app/HomeScreen.tsx @@ -0,0 +1,160 @@ +import React, { useEffect, useState } from 'react'; +import { Linking, SafeAreaView, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { Item } from 'types/Item'; + +const API_URL = "http://192.168.178.22:8000"; + +export default function HomeScreen() { + const [data, setData] = useState([]); + const [selected, setSelected] = useState<'home' | 'royal-road' | 'podcasts' | 'mixtapes'>('home'); + const [menuOpen, setMenuOpen] = useState(false); + + useEffect(() => { + fetch(API_URL) + .then(res => res.json()) + .then(json => { + if (json.status === "ok" && Array.isArray(json.data)) { + setData(json.data); + } + }) + .catch(err => console.error("API error:", err)); + }, []); + + // Filter items for current category + const filteredData = + selected === "home" ? data : data.filter(item => item.category === selected); + + const menuItems = [ + { label: 'Home', key: 'home' }, + { label: 'Royal Road', key: 'royal-road' }, + { label: 'Podcasts', key: 'podcasts' }, + { label: 'Mixtapes', key: 'mixtapes' }, + ]; + + return ( + + {/* Side Menu */} + + {menuItems.map(item => ( + { + setSelected(item.key as any); + setMenuOpen(false); + }} + > + {item.label} + + ))} + + + {/* Main Content */} + + setMenuOpen(!menuOpen)}> + ☰ + + + + {selected === 'home' && 'Welcome to Home!'} + {selected === 'royal-road' && 'Royal Road Updates'} + {selected === 'podcasts' && 'Latest Podcasts'} + {selected === 'mixtapes' && 'New Mixtapes'} + + + + {filteredData.map(item => ( + + {item.title} + {item.info} + Linking.openURL(item.link)}> + Read more + + + ))} + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + flexDirection: 'row', + backgroundColor: '#f2f2f2', + }, + sideMenu: { + width: 0, + backgroundColor: '#333', + paddingTop: 40, + paddingHorizontal: 0, + overflow: 'hidden', + }, + sideMenuOpen: { + width: 180, + paddingHorizontal: 16, + }, + menuItem: { + paddingVertical: 16, + paddingHorizontal: 8, + borderRadius: 4, + marginBottom: 8, + }, + menuItemSelected: { + backgroundColor: '#555', + }, + menuText: { + color: '#fff', + fontSize: 18, + }, + mainContent: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + position: 'relative', + }, + menuButton: { + position: 'absolute', + top: 40, + left: 16, + zIndex: 1, + backgroundColor: '#333', + borderRadius: 20, + padding: 8, + }, + menuButtonText: { + color: '#fff', + fontSize: 24, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + marginTop: 60, + color: '#333', + }, + dataContainer: { + marginTop: 24, + width: '90%', + }, + dataItem: { + backgroundColor: '#fff', + borderRadius: 8, + padding: 16, + marginBottom: 12, + shadowColor: '#000', + shadowOpacity: 0.05, + shadowRadius: 4, + elevation: 2, + }, + dataTitle: { + fontSize: 18, + fontWeight: 'bold', + color: '#222', + marginBottom: 4, + }, + dataDescription: { + fontSize: 15, + color: '#555', + }, +}); \ No newline at end of file diff --git a/app/MainContent.tsx b/app/MainContent.tsx new file mode 100644 index 0000000..6b86202 --- /dev/null +++ b/app/MainContent.tsx @@ -0,0 +1,94 @@ +import React from 'react'; +import { Linking, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { Item } from 'types/Item'; + +type MainContentProps = { + selected: string; + onToggleMenu: () => void; + data: Item[]; +}; + +export default function MainContent({ selected, onToggleMenu, data }: MainContentProps) { + const getTitle = () => { + switch (selected) { + case 'home': return 'Welcome to Home!'; + case 'royal-road': return 'Royal Road Updates'; + case 'podcasts': return 'Latest Podcasts'; + case 'mixtapes': return 'Mixtapes'; + default: return ''; + } + }; + + return ( + + + ☰ + + {getTitle()} + + + {data.map(item => ( + Linking.openURL(item.link)}> + + {item.title} + {item.info} + + + ))} + + + ); +} + +const styles = StyleSheet.create({ + mainContent: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + position: 'relative', + backgroundColor: '#f2f2f2', + }, + menuButton: { + position: 'absolute', + top: 40, + left: 16, + zIndex: 1, + backgroundColor: '#333', + borderRadius: 20, + padding: 8, + }, + menuButtonText: { + color: '#fff', + fontSize: 24, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + color: '#333', + marginTop: 60, + marginBottom: 16, + }, + dataContainer: { + width: '90%', + }, + dataItem: { + backgroundColor: '#fff', + borderRadius: 8, + padding: 16, + marginBottom: 12, + shadowColor: '#000', + shadowOpacity: 0.05, + shadowRadius: 4, + elevation: 2, + }, + dataTitle: { + fontSize: 18, + fontWeight: 'bold', + color: '#222', + marginBottom: 4, + }, + dataDescription: { + fontSize: 15, + color: '#555', + }, +}); diff --git a/app/SideMenu.tsx b/app/SideMenu.tsx new file mode 100644 index 0000000..d34cdcf --- /dev/null +++ b/app/SideMenu.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'; + +export type MenuItem = { + label: string; + key: string; +}; + +type SideMenuProps = { + menuItems: MenuItem[]; + selected: string; + onSelect: (key: string) => void; +}; + +export default function SideMenu({ menuItems, selected, onSelect }: SideMenuProps) { + return ( + + {menuItems.map(item => ( + onSelect(item.key)}> + {item.label} + + ))} + + ); +} + +const styles = StyleSheet.create({ + sideMenu: { + width: 180, + backgroundColor: '#333', + paddingTop: 40, + paddingHorizontal: 16, + }, + menuItem: { + paddingVertical: 16, + borderRadius: 4, + marginBottom: 8, + }, + menuItemSelected: { + backgroundColor: '#555', + }, + menuText: { + color: '#fff', + fontSize: 18, + }, +}); diff --git a/app/index.tsx b/app/index.tsx new file mode 100644 index 0000000..0b526c4 --- /dev/null +++ b/app/index.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import HomeScreen from './HomeScreen'; + +export default function App() { + return ; +} \ No newline at end of file diff --git a/assets/images/android-icon-background.png b/assets/images/android-icon-background.png new file mode 100644 index 0000000..5ffefc5 Binary files /dev/null and b/assets/images/android-icon-background.png differ diff --git a/assets/images/android-icon-foreground.png b/assets/images/android-icon-foreground.png new file mode 100644 index 0000000..3a9e501 Binary files /dev/null and b/assets/images/android-icon-foreground.png differ diff --git a/assets/images/android-icon-monochrome.png b/assets/images/android-icon-monochrome.png new file mode 100644 index 0000000..77484eb Binary files /dev/null and b/assets/images/android-icon-monochrome.png differ diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000..408bd74 Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/images/icon.png b/assets/images/icon.png new file mode 100644 index 0000000..7165a53 Binary files /dev/null and b/assets/images/icon.png differ diff --git a/assets/images/partial-react-logo.png b/assets/images/partial-react-logo.png new file mode 100644 index 0000000..66fd957 Binary files /dev/null and b/assets/images/partial-react-logo.png differ diff --git a/assets/images/react-logo.png b/assets/images/react-logo.png new file mode 100644 index 0000000..9d72a9f Binary files /dev/null and b/assets/images/react-logo.png differ diff --git a/assets/images/react-logo@2x.png b/assets/images/react-logo@2x.png new file mode 100644 index 0000000..2229b13 Binary files /dev/null and b/assets/images/react-logo@2x.png differ diff --git a/assets/images/react-logo@3x.png b/assets/images/react-logo@3x.png new file mode 100644 index 0000000..a99b203 Binary files /dev/null and b/assets/images/react-logo@3x.png differ diff --git a/assets/images/splash-icon.png b/assets/images/splash-icon.png new file mode 100644 index 0000000..03d6f6b Binary files /dev/null and b/assets/images/splash-icon.png differ diff --git a/backup_package.json b/backup_package.json new file mode 100644 index 0000000..70eeb00 --- /dev/null +++ b/backup_package.json @@ -0,0 +1,49 @@ +{ + "name": "my-drawer-app", + "main": "expo-router/entry", + "version": "1.0.0", + "scripts": { + "start": "expo start", + "reset-project": "node ./scripts/reset-project.js", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web", + "lint": "expo lint" + }, + "dependencies": { + "@expo/metro-runtime": "~6.1.2", + "@expo/vector-icons": "^15.0.2", + "@react-native-firebase/app": "^23.4.0", + "@react-navigation/bottom-tabs": "^7.4.0", + "@react-navigation/elements": "^2.6.3", + "@react-navigation/native": "^7.1.8", + "expo": "~54.0.10", + "expo-constants": "~18.0.9", + "expo-font": "~14.0.8", + "expo-haptics": "~15.0.7", + "expo-image": "~3.0.8", + "expo-linking": "~8.0.8", + "expo-router": "~6.0.8", + "expo-splash-screen": "~31.0.10", + "expo-status-bar": "~3.0.8", + "expo-symbols": "~1.0.7", + "expo-system-ui": "~6.0.7", + "expo-web-browser": "~15.0.7", + "react": "19.1.0", + "react-dom": "19.1.0", + "react-native": "0.81.4", + "react-native-gesture-handler": "~2.28.0", + "react-native-reanimated": "~4.1.1", + "react-native-safe-area-context": "~5.6.0", + "react-native-screens": "~4.16.0", + "react-native-web": "~0.21.0", + "react-native-worklets": "0.5.1" + }, + "devDependencies": { + "@types/react": "~19.1.0", + "eslint": "^9.25.0", + "eslint-config-expo": "~10.0.0", + "typescript": "~5.9.2" + }, + "private": true +} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..5025da6 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,10 @@ +// https://docs.expo.dev/guides/using-eslint/ +const { defineConfig } = require('eslint/config'); +const expoConfig = require('eslint-config-expo/flat'); + +module.exports = defineConfig([ + expoConfig, + { + ignores: ['dist/*'], + }, +]); diff --git a/package.json b/package.json new file mode 100644 index 0000000..2e5307d --- /dev/null +++ b/package.json @@ -0,0 +1,44 @@ +{ + "name": "my-drawer-app", + "main": "expo-router/entry", + "version": "1.0.0", + "scripts": { + "start": "expo start", + "reset-project": "node ./scripts/reset-project.js", + "android": "expo start --android", + "ios": "expo start --ios", + "web": "expo start --web", + "lint": "expo lint" + }, + "dependencies": { + "@expo/metro-runtime": "~6.1.2", + "@expo/vector-icons": "^15.0.2", + "expo": "~54.0.10", + "expo-constants": "~18.0.9", + "expo-font": "~14.0.8", + "expo-haptics": "~15.0.7", + "expo-image": "~3.0.8", + "expo-linking": "~8.0.8", + "expo-router": "~6.0.8", + "expo-splash-screen": "~31.0.10", + "expo-status-bar": "~3.0.8", + "expo-symbols": "~1.0.7", + "expo-system-ui": "~6.0.7", + "expo-web-browser": "~15.0.7", + "react": "19.1.0", + "react-dom": "19.1.0", + "react-native": "0.81.4", + "react-native-gesture-handler": "~2.28.0", + "react-native-reanimated": "~4.1.1", + "react-native-safe-area-context": "~5.6.0", + "react-native-screens": "~4.16.0", + "react-native-web": "~0.21.0" + }, + "devDependencies": { + "@types/react": "~19.1.0", + "eslint": "^9.25.0", + "eslint-config-expo": "~10.0.0", + "typescript": "~5.9.2" + }, + "private": true +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..aa9aac9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "expo/tsconfig.base", + "compilerOptions": { + "strict": true, + "paths": { + "@/*": [ + "./*" + ], + "types/*": [ + "./types/*" + ] + } + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ".expo/types/**/*.ts", + "expo-env.d.ts" + ] +} diff --git a/types/Item.ts b/types/Item.ts new file mode 100644 index 0000000..84877dd --- /dev/null +++ b/types/Item.ts @@ -0,0 +1,7 @@ +export type Item = { + timestamp: number; + category: 'home' | 'royal-road' | 'podcasts' | 'mixtapes'; + title: string; + info: string; + link: string; +}; \ No newline at end of file