From 34b89b54c4c9c7cea077caa1be4984af673d98ee Mon Sep 17 00:00:00 2001 From: yuchenglong Date: Mon, 12 Jan 2026 15:31:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=BB=E9=A2=98=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E3=80=81=E5=A4=9A=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(tabs)/_layout.tsx | 71 +++++++-- app/(tabs)/explore.tsx | 112 ------------- app/(tabs)/favorite.tsx | 19 +++ app/(tabs)/finished.tsx | 19 +++ app/(tabs)/index.tsx | 102 ++---------- app/(tabs)/live.tsx | 19 +++ app/(tabs)/upcoming.tsx | 19 +++ app/_layout.tsx | 18 ++- app/profile.tsx | 193 +++++++++++++++++++++++ assets/images/partial-react-logo.png | Bin 5075 -> 0 bytes assets/images/react-logo.png | Bin 6341 -> 0 bytes assets/images/react-logo@2x.png | Bin 14225 -> 0 bytes assets/images/react-logo@3x.png | Bin 21252 -> 0 bytes components/external-link.tsx | 25 --- components/hello-wave.tsx | 19 --- components/home-header.tsx | 73 +++++++++ components/parallax-scroll-view.tsx | 79 ---------- components/ui/collapsible.tsx | 45 ------ components/ui/icon-symbol.ios.tsx | 32 ---- components/ui/icon-symbol.tsx | 34 ++-- components/ui/tab-bar-background.ios.tsx | 22 +++ components/ui/tab-bar-background.tsx | 6 + context/ThemeContext.tsx | 115 ++++++++++++++ hooks/use-color-scheme.ts | 12 +- i18n/index.ts | 55 +++++++ i18n/locales/en.json | 28 ++++ i18n/locales/zh.json | 28 ++++ package.json | 3 +- scripts/reset-project.js | 112 ------------- 29 files changed, 702 insertions(+), 558 deletions(-) delete mode 100644 app/(tabs)/explore.tsx create mode 100644 app/(tabs)/favorite.tsx create mode 100644 app/(tabs)/finished.tsx create mode 100644 app/(tabs)/live.tsx create mode 100644 app/(tabs)/upcoming.tsx create mode 100644 app/profile.tsx delete mode 100644 assets/images/partial-react-logo.png delete mode 100644 assets/images/react-logo.png delete mode 100644 assets/images/react-logo@2x.png delete mode 100644 assets/images/react-logo@3x.png delete mode 100644 components/external-link.tsx delete mode 100644 components/hello-wave.tsx create mode 100644 components/home-header.tsx delete mode 100644 components/parallax-scroll-view.tsx delete mode 100644 components/ui/collapsible.tsx delete mode 100644 components/ui/icon-symbol.ios.tsx create mode 100644 components/ui/tab-bar-background.ios.tsx create mode 100644 components/ui/tab-bar-background.tsx create mode 100644 context/ThemeContext.tsx create mode 100644 i18n/index.ts create mode 100644 i18n/locales/en.json create mode 100644 i18n/locales/zh.json delete mode 100644 scripts/reset-project.js diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 54e11d0..6a5a436 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -1,33 +1,76 @@ -import { Tabs } from 'expo-router'; -import React from 'react'; +import { Tabs } from "expo-router"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Platform } from "react-native"; -import { HapticTab } from '@/components/haptic-tab'; -import { IconSymbol } from '@/components/ui/icon-symbol'; -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; +import { HapticTab } from "@/components/haptic-tab"; +import { IconSymbol } from "@/components/ui/icon-symbol"; +import TabBarBackground from "@/components/ui/tab-bar-background"; +import { Colors } from "@/constants/theme"; +import { useTheme } from "@/context/ThemeContext"; export default function TabLayout() { - const colorScheme = useColorScheme(); + const { theme } = useTheme(); + const { t } = useTranslation(); return ( + tabBarBackground: TabBarBackground, + tabBarStyle: Platform.select({ + ios: { + position: "absolute", + }, + default: {}, + }), + }} + > , + title: t("tabs.all"), + tabBarIcon: ({ color }) => ( + + ), }} /> , + title: t("tabs.live"), + tabBarIcon: ({ color }) => ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), + }} + /> + ( + + ), }} /> diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx deleted file mode 100644 index 71518f9..0000000 --- a/app/(tabs)/explore.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } from 'react-native'; - -import { Collapsible } from '@/components/ui/collapsible'; -import { ExternalLink } from '@/components/external-link'; -import ParallaxScrollView from '@/components/parallax-scroll-view'; -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { IconSymbol } from '@/components/ui/icon-symbol'; -import { Fonts } from '@/constants/theme'; - -export default function TabTwoScreen() { - return ( - - }> - - - Explore - - - This app includes example code to help you get started. - - - This app has two screens:{' '} - app/(tabs)/index.tsx and{' '} - app/(tabs)/explore.tsx - - - The layout file in app/(tabs)/_layout.tsx{' '} - sets up the tab navigator. - - - Learn more - - - - - You can open this project on Android, iOS, and the web. To open the web version, press{' '} - w in the terminal running this project. - - - - - For static images, you can use the @2x and{' '} - @3x suffixes to provide files for - different screen densities - - - - Learn more - - - - - This template has light and dark mode support. The{' '} - useColorScheme() hook lets you inspect - what the user's current color scheme is, and so you can adjust UI colors accordingly. - - - Learn more - - - - - This template includes an example of an animated component. The{' '} - components/HelloWave.tsx component uses - the powerful{' '} - - react-native-reanimated - {' '} - library to create a waving hand animation. - - {Platform.select({ - ios: ( - - The components/ParallaxScrollView.tsx{' '} - component provides a parallax effect for the header image. - - ), - })} - - - ); -} - -const styles = StyleSheet.create({ - headerImage: { - color: '#808080', - bottom: -90, - left: -35, - position: 'absolute', - }, - titleContainer: { - flexDirection: 'row', - gap: 8, - }, -}); diff --git a/app/(tabs)/favorite.tsx b/app/(tabs)/favorite.tsx new file mode 100644 index 0000000..e4e9b6e --- /dev/null +++ b/app/(tabs)/favorite.tsx @@ -0,0 +1,19 @@ +import { ThemedText } from "@/components/themed-text"; +import { ThemedView } from "@/components/themed-view"; +import { StyleSheet } from "react-native"; + +export default function FavoriteScreen() { + return ( + + Favorites + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, +}); diff --git a/app/(tabs)/finished.tsx b/app/(tabs)/finished.tsx new file mode 100644 index 0000000..c10a665 --- /dev/null +++ b/app/(tabs)/finished.tsx @@ -0,0 +1,19 @@ +import { ThemedText } from "@/components/themed-text"; +import { ThemedView } from "@/components/themed-view"; +import { StyleSheet } from "react-native"; + +export default function FinishedScreen() { + return ( + + Finished Events + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, +}); diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 786b736..7c649c2 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,98 +1,24 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } from 'react-native'; - -import { HelloWave } from '@/components/hello-wave'; -import ParallaxScrollView from '@/components/parallax-scroll-view'; -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { Link } from 'expo-router'; +import { HomeHeader } from "@/components/home-header"; +import { ThemedView } from "@/components/themed-view"; +import React from "react"; +import { ScrollView, StyleSheet } from "react-native"; export default function HomeScreen() { return ( - - }> - - Welcome! - - - - Step 1: Try it - - Edit app/(tabs)/index.tsx to see changes. - Press{' '} - - {Platform.select({ - ios: 'cmd + d', - android: 'cmd + m', - web: 'F12', - })} - {' '} - to open developer tools. - - - - - - Step 2: Explore - - - - alert('Action pressed')} /> - alert('Share pressed')} - /> - - alert('Delete pressed')} - /> - - - - - - {`Tap the Explore tab to learn more about what's included in this starter app.`} - - - - Step 3: Get a fresh start - - {`When you're ready, run `} - npm run reset-project to get a fresh{' '} - app directory. This will move the current{' '} - app to{' '} - app-example. - - - + + + + {/* Placeholder for future content */} + + ); } const styles = StyleSheet.create({ - titleContainer: { - flexDirection: 'row', - alignItems: 'center', - gap: 8, + container: { + flex: 1, }, - stepContainer: { - gap: 8, - marginBottom: 8, - }, - reactLogo: { - height: 178, - width: 290, - bottom: 0, - left: 0, - position: 'absolute', + content: { + padding: 16, }, }); diff --git a/app/(tabs)/live.tsx b/app/(tabs)/live.tsx new file mode 100644 index 0000000..a27240a --- /dev/null +++ b/app/(tabs)/live.tsx @@ -0,0 +1,19 @@ +import { ThemedText } from "@/components/themed-text"; +import { ThemedView } from "@/components/themed-view"; +import { StyleSheet } from "react-native"; + +export default function LiveScreen() { + return ( + + Live Streams + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, +}); diff --git a/app/(tabs)/upcoming.tsx b/app/(tabs)/upcoming.tsx new file mode 100644 index 0000000..c92bd5e --- /dev/null +++ b/app/(tabs)/upcoming.tsx @@ -0,0 +1,19 @@ +import { ThemedText } from "@/components/themed-text"; +import { ThemedView } from "@/components/themed-view"; +import { StyleSheet } from "react-native"; + +export default function UpcomingScreen() { + return ( + + Upcoming Events + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: "center", + justifyContent: "center", + }, +}); diff --git a/app/_layout.tsx b/app/_layout.tsx index f518c9b..9ff029f 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,24 +1,34 @@ -import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; +import { DarkTheme, DefaultTheme, ThemeProvider as NavigationThemeProvider } from '@react-navigation/native'; import { Stack } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import 'react-native-reanimated'; import { useColorScheme } from '@/hooks/use-color-scheme'; +import { ThemeProvider } from '@/context/ThemeContext'; +import '@/i18n'; // Initialize i18n export const unstable_settings = { anchor: '(tabs)', }; export default function RootLayout() { + return ( + + + + ); +} + +function RootLayoutNav() { const colorScheme = useColorScheme(); return ( - + - - + + ); } diff --git a/app/profile.tsx b/app/profile.tsx new file mode 100644 index 0000000..327e33b --- /dev/null +++ b/app/profile.tsx @@ -0,0 +1,193 @@ +import { Image } from "expo-image"; +import { Stack } from "expo-router"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { ScrollView, StyleSheet, TouchableOpacity, View } from "react-native"; + +import { ThemedText } from "@/components/themed-text"; +import { IconSymbol } from "@/components/ui/icon-symbol"; +import { useTheme } from "@/context/ThemeContext"; +import { changeLanguage } from "@/i18n"; + +export default function ProfileScreen() { + const { theme, toggleTheme, setTheme, isSystemTheme, useSystemTheme } = + useTheme(); + const { t, i18n } = useTranslation(); + const isDark = theme === "dark"; + + const currentLanguage = i18n.language; + + const toggleLanguage = () => { + const nextLang = currentLanguage.startsWith("en") ? "zh" : "en"; + changeLanguage(nextLang); + }; + + const iconColor = isDark ? "#FFFFFF" : "#000000"; + const textColor = isDark ? "#FFFFFF" : "#000000"; + const subTextColor = isDark ? "#AAAAAA" : "#666666"; + + return ( + <> + + + {/* User Info Section */} + + + + + {t("profile.name")} + + user@example.com + + + + + + {/* Settings Section */} + + {t("settings.title")} + + + + {/* Theme Setting */} + + + + {t("settings.theme")} + + + + + {theme === "light" ? t("settings.light") : t("settings.dark")} + + + + + + {/* Language Setting */} + + + + {t("settings.language")} + + + + + {currentLanguage.startsWith("zh") + ? t("settings.chinese") + : t("settings.english")} + + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + section: { + marginTop: 20, + paddingVertical: 10, + paddingHorizontal: 16, + borderRadius: 10, // iOS style groups + marginHorizontal: 16, + }, + sectionTitle: { + marginLeft: 32, + marginTop: 20, + marginBottom: 5, + fontSize: 13, + textTransform: "uppercase", + opacity: 0.6, + }, + profileHeader: { + flexDirection: "row", + alignItems: "center", + paddingVertical: 10, + }, + avatar: { + width: 60, + height: 60, + borderRadius: 30, + backgroundColor: "#ccc", + }, + profileInfo: { + marginLeft: 15, + }, + settingItem: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + paddingVertical: 12, + }, + settingLabel: { + flexDirection: "row", + alignItems: "center", + }, + settingControl: { + flexDirection: "row", + alignItems: "center", + }, + button: { + paddingHorizontal: 10, + paddingVertical: 5, + }, +}); diff --git a/assets/images/partial-react-logo.png b/assets/images/partial-react-logo.png deleted file mode 100644 index 66fd9570e4fac42bca15352def191c563100b2ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5075 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 diff --git a/assets/images/react-logo.png b/assets/images/react-logo.png deleted file mode 100644 index 9d72a9ffcbb39d89709073e1a7edd8ba414932c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6341 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

OG^bt6yG2??7FhdGVc8feK}syBta)V!iG4 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, 'href'> & { href: Href & string }; - -export function ExternalLink({ href, ...rest }: Props) { - return ( - { - if (process.env.EXPO_OS !== 'web') { - // Prevent the default behavior of linking to the default browser on native. - event.preventDefault(); - // Open the link in an in-app browser. - await openBrowserAsync(href, { - presentationStyle: WebBrowserPresentationStyle.AUTOMATIC, - }); - } - }} - /> - ); -} diff --git a/components/hello-wave.tsx b/components/hello-wave.tsx deleted file mode 100644 index 5def547..0000000 --- a/components/hello-wave.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import Animated from 'react-native-reanimated'; - -export function HelloWave() { - return ( - - 👋 - - ); -} diff --git a/components/home-header.tsx b/components/home-header.tsx new file mode 100644 index 0000000..406144b --- /dev/null +++ b/components/home-header.tsx @@ -0,0 +1,73 @@ +import { ThemedText } from "@/components/themed-text"; +import { ThemedView } from "@/components/themed-view"; +import { IconSymbol } from "@/components/ui/icon-symbol"; +import { Colors } from "@/constants/theme"; +import { useTheme } from "@/context/ThemeContext"; +import { useRouter } from "expo-router"; +import React from "react"; +import { useTranslation } from "react-i18next"; +import { StyleSheet, TouchableOpacity } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +export function HomeHeader() { + const router = useRouter(); + const { theme } = useTheme(); + const { t } = useTranslation(); + const iconColor = theme === "light" ? Colors.light.icon : Colors.dark.icon; + + return ( + + + + + {t("home.title")} + + + { + /* TODO: Search Action */ + }} + > + + + router.push("/profile")} + > + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + paddingTop: 0, + }, + safeArea: { + paddingBottom: 10, + paddingHorizontal: 16, + }, + headerContent: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + paddingTop: 8, + }, + title: { + fontSize: 24, + fontWeight: "bold", + }, + actions: { + flexDirection: "row", + gap: 16, + alignItems: "center", + }, + iconButton: { + padding: 4, + }, +}); diff --git a/components/parallax-scroll-view.tsx b/components/parallax-scroll-view.tsx deleted file mode 100644 index 6f674a7..0000000 --- a/components/parallax-scroll-view.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import type { PropsWithChildren, ReactElement } from 'react'; -import { StyleSheet } from 'react-native'; -import Animated, { - interpolate, - useAnimatedRef, - useAnimatedStyle, - useScrollOffset, -} from 'react-native-reanimated'; - -import { ThemedView } from '@/components/themed-view'; -import { useColorScheme } from '@/hooks/use-color-scheme'; -import { useThemeColor } from '@/hooks/use-theme-color'; - -const HEADER_HEIGHT = 250; - -type Props = PropsWithChildren<{ - headerImage: ReactElement; - headerBackgroundColor: { dark: string; light: string }; -}>; - -export default function ParallaxScrollView({ - children, - headerImage, - headerBackgroundColor, -}: Props) { - const backgroundColor = useThemeColor({}, 'background'); - const colorScheme = useColorScheme() ?? 'light'; - const scrollRef = useAnimatedRef(); - const scrollOffset = useScrollOffset(scrollRef); - const headerAnimatedStyle = useAnimatedStyle(() => { - return { - transform: [ - { - translateY: interpolate( - scrollOffset.value, - [-HEADER_HEIGHT, 0, HEADER_HEIGHT], - [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75] - ), - }, - { - scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]), - }, - ], - }; - }); - - return ( - - - {headerImage} - - {children} - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - header: { - height: HEADER_HEIGHT, - overflow: 'hidden', - }, - content: { - flex: 1, - padding: 32, - gap: 16, - overflow: 'hidden', - }, -}); diff --git a/components/ui/collapsible.tsx b/components/ui/collapsible.tsx deleted file mode 100644 index 6345fde..0000000 --- a/components/ui/collapsible.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { PropsWithChildren, useState } from 'react'; -import { StyleSheet, TouchableOpacity } from 'react-native'; - -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { IconSymbol } from '@/components/ui/icon-symbol'; -import { Colors } from '@/constants/theme'; -import { useColorScheme } from '@/hooks/use-color-scheme'; - -export function Collapsible({ children, title }: PropsWithChildren & { title: string }) { - const [isOpen, setIsOpen] = useState(false); - const theme = useColorScheme() ?? 'light'; - - return ( - - setIsOpen((value) => !value)} - activeOpacity={0.8}> - - - {title} - - {isOpen && {children}} - - ); -} - -const styles = StyleSheet.create({ - heading: { - flexDirection: 'row', - alignItems: 'center', - gap: 6, - }, - content: { - marginTop: 6, - marginLeft: 24, - }, -}); diff --git a/components/ui/icon-symbol.ios.tsx b/components/ui/icon-symbol.ios.tsx deleted file mode 100644 index 9177f4d..0000000 --- a/components/ui/icon-symbol.ios.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; -import { StyleProp, ViewStyle } from 'react-native'; - -export function IconSymbol({ - name, - size = 24, - color, - style, - weight = 'regular', -}: { - name: SymbolViewProps['name']; - size?: number; - color: string; - style?: StyleProp; - weight?: SymbolWeight; -}) { - return ( - - ); -} diff --git a/components/ui/icon-symbol.tsx b/components/ui/icon-symbol.tsx index b7ece6b..aaba32f 100644 --- a/components/ui/icon-symbol.tsx +++ b/components/ui/icon-symbol.tsx @@ -1,29 +1,13 @@ -// Fallback for using MaterialIcons on Android and web. +// Cross-platform icon component using Ionicons for all platforms. +import Ionicons from "@expo/vector-icons/Ionicons"; +import { ComponentProps } from "react"; +import { OpaqueColorValue, StyleProp, TextStyle } from "react-native"; -import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import { SymbolWeight, SymbolViewProps } from 'expo-symbols'; -import { ComponentProps } from 'react'; -import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native'; - -type IconMapping = Record['name']>; -type IconSymbolName = keyof typeof MAPPING; +// Define the icon names we use in the app +export type IconSymbolName = ComponentProps["name"]; /** - * Add your SF Symbols to Material Icons mappings here. - * - see Material Icons in the [Icons Directory](https://icons.expo.fyi). - * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app. - */ -const MAPPING = { - 'house.fill': 'home', - 'paperplane.fill': 'send', - 'chevron.left.forwardslash.chevron.right': 'code', - 'chevron.right': 'chevron-right', -} as IconMapping; - -/** - * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web. - * This ensures a consistent look across platforms, and optimal resource usage. - * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons. + * An icon component that uses Ionicons on all platforms. */ export function IconSymbol({ name, @@ -35,7 +19,7 @@ export function IconSymbol({ size?: number; color: string | OpaqueColorValue; style?: StyleProp; - weight?: SymbolWeight; + weight?: string; // Kept for compatibility but unused }) { - return ; + return ; } diff --git a/components/ui/tab-bar-background.ios.tsx b/components/ui/tab-bar-background.ios.tsx new file mode 100644 index 0000000..a0b6f70 --- /dev/null +++ b/components/ui/tab-bar-background.ios.tsx @@ -0,0 +1,22 @@ +import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; +import { BlurView } from "expo-blur"; +import { StyleSheet } from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; + +export default function BlurTabBarBackground() { + return ( + + ); +} + +export function useBottomTabOverflow() { + const tabHeight = useBottomTabBarHeight(); + const { bottom } = useSafeAreaInsets(); + return tabHeight - bottom; +} diff --git a/components/ui/tab-bar-background.tsx b/components/ui/tab-bar-background.tsx new file mode 100644 index 0000000..70d1c3c --- /dev/null +++ b/components/ui/tab-bar-background.tsx @@ -0,0 +1,6 @@ +// This is a shim for web and Android where the tab bar is generally opaque. +export default undefined; + +export function useBottomTabOverflow() { + return 0; +} diff --git a/context/ThemeContext.tsx b/context/ThemeContext.tsx new file mode 100644 index 0000000..2347405 --- /dev/null +++ b/context/ThemeContext.tsx @@ -0,0 +1,115 @@ +import AsyncStorage from "@react-native-async-storage/async-storage"; +import React, { createContext, useContext, useEffect, useState } from "react"; +import { useColorScheme as useDeviceColorScheme } from "react-native"; + +type Theme = "light" | "dark"; + +interface ThemeContextType { + theme: Theme; + toggleTheme: () => void; + setTheme: (theme: Theme) => void; + systemTheme: Theme; + isSystemTheme: boolean; + useSystemTheme: () => void; +} + +const ThemeContext = createContext(undefined); + +const THEME_STORAGE_KEY = "user_theme_preference"; + +export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ + children, +}) => { + const deviceColorScheme = useDeviceColorScheme(); + const systemTheme = deviceColorScheme === "dark" ? "dark" : "light"; + + // State to hold the current theme + const [theme, setThemeState] = useState(systemTheme); + // State to track if we are following system theme + const [isSystemTheme, setIsSystemTheme] = useState(true); + + useEffect(() => { + loadThemePersistence(); + }, []); + + useEffect(() => { + // If we are following system theme, update when device theme changes + if (isSystemTheme) { + setThemeState(systemTheme); + } + }, [systemTheme, isSystemTheme]); + + const loadThemePersistence = async () => { + try { + const storedTheme = await AsyncStorage.getItem(THEME_STORAGE_KEY); + if (storedTheme) { + if (storedTheme === "system") { + setIsSystemTheme(true); + setThemeState(systemTheme); + } else { + setIsSystemTheme(false); + setThemeState(storedTheme as Theme); + } + } else { + // Default to system + setIsSystemTheme(true); + setThemeState(systemTheme); + } + } catch (error) { + console.warn("Failed to load theme preference", error); + // Fallback to system + setIsSystemTheme(true); + setThemeState(systemTheme); + } + }; + + const saveThemePreference = async (newTheme: Theme | "system") => { + try { + await AsyncStorage.setItem(THEME_STORAGE_KEY, newTheme); + } catch (error) { + console.warn("Failed to save theme preference", error); + } + }; + + const toggleTheme = () => { + const newTheme = theme === "light" ? "dark" : "light"; + setIsSystemTheme(false); + setThemeState(newTheme); + saveThemePreference(newTheme); + }; + + const setTheme = (newTheme: Theme) => { + setIsSystemTheme(false); + setThemeState(newTheme); + saveThemePreference(newTheme); + }; + + const useSystemTheme = () => { + setIsSystemTheme(true); + setThemeState(systemTheme); + saveThemePreference("system"); + }; + + return ( + + {children} + + ); +}; + +export const useTheme = () => { + const context = useContext(ThemeContext); + if (!context) { + throw new Error("useTheme must be used within a ThemeProvider"); + } + return context; +}; diff --git a/hooks/use-color-scheme.ts b/hooks/use-color-scheme.ts index 17e3c63..0074ac0 100644 --- a/hooks/use-color-scheme.ts +++ b/hooks/use-color-scheme.ts @@ -1 +1,11 @@ -export { useColorScheme } from 'react-native'; +import { useTheme } from "@/context/ThemeContext"; +import { ColorSchemeName } from "react-native"; + +export function useColorScheme(): ColorSchemeName { + try { + const { theme } = useTheme(); + return theme; + } catch (e) { + return "light"; + } +} diff --git a/i18n/index.ts b/i18n/index.ts new file mode 100644 index 0000000..9667bcf --- /dev/null +++ b/i18n/index.ts @@ -0,0 +1,55 @@ +import AsyncStorage from "@react-native-async-storage/async-storage"; +import * as Localization from "expo-localization"; +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; + +import en from "./locales/en.json"; +import zh from "./locales/zh.json"; + +const RESOURCES = { + en: { translation: en }, + zh: { translation: zh }, +}; + +const LANGUAGE_STORAGE_KEY = "user_language_preference"; + +const initI18n = async () => { + let savedLanguage = null; + try { + savedLanguage = await AsyncStorage.getItem(LANGUAGE_STORAGE_KEY); + } catch (error) { + console.warn("Failed to load language preference", error); + } + + // Get device language (e.g. "en-US" -> "en") + // Prefer Localization.getLocales()[0].languageCode; fall back to 'en' + const locales = Localization.getLocales?.() ?? []; + const deviceLanguageCode = locales[0]?.languageCode ?? "en"; + + const languageToUse = savedLanguage || deviceLanguageCode || "en"; + + i18n.use(initReactI18next).init({ + resources: RESOURCES, + lng: languageToUse, + fallbackLng: "en", + interpolation: { + escapeValue: false, + }, + react: { + useSuspense: false, // For React Native compatibility + }, + }); +}; + +initI18n(); + +export default i18n; + +export const changeLanguage = async (lang: string) => { + try { + await i18n.changeLanguage(lang); + await AsyncStorage.setItem(LANGUAGE_STORAGE_KEY, lang); + } catch (error) { + console.warn("Failed to change/save language", error); + } +}; diff --git a/i18n/locales/en.json b/i18n/locales/en.json new file mode 100644 index 0000000..ff44367 --- /dev/null +++ b/i18n/locales/en.json @@ -0,0 +1,28 @@ +{ + "welcome": "Welcome!", + "tabs": { + "all": "All", + "live": "Live", + "upcoming": "Upcoming", + "finished": "Finished", + "fav": "Favorites" + }, + "settings": { + "title": "Settings", + "theme": "Theme", + "language": "Language", + "light": "Light", + "dark": "Dark", + "system": "System", + "english": "English", + "chinese": "Chinese" + }, + "home": { + "title": "Physical" + }, + "profile": { + "title": "My Profile", + "name": "User Name", + "settings": "Settings" + } +} \ No newline at end of file diff --git a/i18n/locales/zh.json b/i18n/locales/zh.json new file mode 100644 index 0000000..ae3fd2e --- /dev/null +++ b/i18n/locales/zh.json @@ -0,0 +1,28 @@ +{ + "welcome": "欢迎!", + "tabs": { + "all": "全部", + "live": "直播", + "upcoming": "即将到来", + "finished": "已完成", + "fav": "收藏" + }, + "settings": { + "title": "设置", + "theme": "主题", + "language": "语言", + "light": "浅色", + "dark": "深色", + "system": "跟随系统", + "english": "英文", + "chinese": "中文" + }, + "home": { + "title": "Physical" + }, + "profile": { + "title": "我的", + "name": "用户名", + "settings": "设置" + } +} \ No newline at end of file diff --git a/package.json b/package.json index 2197716..f0cc777 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "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", @@ -59,4 +58,4 @@ "typescript": "~5.9.2" }, "private": true -} +} \ No newline at end of file diff --git a/scripts/reset-project.js b/scripts/reset-project.js deleted file mode 100644 index 51dff15..0000000 --- a/scripts/reset-project.js +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file. - * You can remove the `reset-project` script from package.json and safely delete this file after running it. - */ - -const fs = require("fs"); -const path = require("path"); -const readline = require("readline"); - -const root = process.cwd(); -const oldDirs = ["app", "components", "hooks", "constants", "scripts"]; -const exampleDir = "app-example"; -const newAppDir = "app"; -const exampleDirPath = path.join(root, exampleDir); - -const indexContent = `import { Text, View } from "react-native"; - -export default function Index() { - return ( - - Edit app/index.tsx to edit this screen. - - ); -} -`; - -const layoutContent = `import { Stack } from "expo-router"; - -export default function RootLayout() { - return ; -} -`; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); - -const moveDirectories = async (userInput) => { - try { - if (userInput === "y") { - // Create the app-example directory - await fs.promises.mkdir(exampleDirPath, { recursive: true }); - console.log(`📁 /${exampleDir} directory created.`); - } - - // Move old directories to new app-example directory or delete them - for (const dir of oldDirs) { - const oldDirPath = path.join(root, dir); - if (fs.existsSync(oldDirPath)) { - if (userInput === "y") { - const newDirPath = path.join(root, exampleDir, dir); - await fs.promises.rename(oldDirPath, newDirPath); - console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`); - } else { - await fs.promises.rm(oldDirPath, { recursive: true, force: true }); - console.log(`❌ /${dir} deleted.`); - } - } else { - console.log(`➡️ /${dir} does not exist, skipping.`); - } - } - - // Create new /app directory - const newAppDirPath = path.join(root, newAppDir); - await fs.promises.mkdir(newAppDirPath, { recursive: true }); - console.log("\n📁 New /app directory created."); - - // Create index.tsx - const indexPath = path.join(newAppDirPath, "index.tsx"); - await fs.promises.writeFile(indexPath, indexContent); - console.log("📄 app/index.tsx created."); - - // Create _layout.tsx - const layoutPath = path.join(newAppDirPath, "_layout.tsx"); - await fs.promises.writeFile(layoutPath, layoutContent); - console.log("📄 app/_layout.tsx created."); - - console.log("\n✅ Project reset complete. Next steps:"); - console.log( - `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${ - userInput === "y" - ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` - : "" - }` - ); - } catch (error) { - console.error(`❌ Error during script execution: ${error.message}`); - } -}; - -rl.question( - "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ", - (answer) => { - const userInput = answer.trim().toLowerCase() || "y"; - if (userInput === "y" || userInput === "n") { - moveDirectories(userInput).finally(() => rl.close()); - } else { - console.log("❌ Invalid input. Please enter 'Y' or 'N'."); - rl.close(); - } - } -);