diff --git a/.gitea/workflows/build-apk.yml b/.gitea/workflows/build-apk.yml index 36fa843..96e51e8 100644 --- a/.gitea/workflows/build-apk.yml +++ b/.gitea/workflows/build-apk.yml @@ -53,12 +53,11 @@ jobs: - name: Create Release uses: https://gitea.com/actions/gitea-release-action@v1 - working-directory: . + working-directory: dist with: tag_name: latest name: Latest Build - overwrite_files: true files: | - - dist/build.zip + - build.zip env: GITEA_TOKEN: ${{ secrets.GITEA }} \ No newline at end of file diff --git a/apk/app/(auth)/_layout.tsx b/apk/app/(auth)/_layout.tsx new file mode 100644 index 0000000..73c2754 --- /dev/null +++ b/apk/app/(auth)/_layout.tsx @@ -0,0 +1,18 @@ +import { Stack, useRouter } from "expo-router"; +import { useAuth } from "@/context/auth-context"; +import { useEffect } from "react"; + +export default function AuthLayout() { + const { user } = useAuth(); + const router = useRouter(); + + useEffect(() => { + if (user) { + router.replace("/(tabs)"); + } else { + router.replace("/(auth)/login"); + } + }, [user]); + + return ; +} \ No newline at end of file diff --git a/apk/app/(auth)/login.tsx b/apk/app/(auth)/login.tsx new file mode 100644 index 0000000..078099c --- /dev/null +++ b/apk/app/(auth)/login.tsx @@ -0,0 +1,145 @@ +import React, { useState } from "react"; +import { View, Text, TextInput, TouchableOpacity, StyleSheet } from "react-native"; +import { useAuth } from "@/context/auth-context"; + +export default function AuthScreen() { + const [isLogin, setIsLogin] = useState(true); + + return isLogin ? ( + setIsLogin(false)} /> + ) : ( + setIsLogin(true)} /> + ); +} + +function LoginScreen({ onSwitch }) { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const { login } = useAuth(); + + const handleLogin = () => { + console.log("Login ->", { email, password }); + login(email, password); + }; + + return ( + + Welcome Back + + + + + + + Login + + + + Don't have an account? Register + + + ); +} + +function RegisterScreen({ onSwitch }) { + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const handleRegister = () => { + console.log("Register ->", { name, email, password }); + }; + + return ( + + Create Account + + + + + + + + + Register + + + + Already have an account? Login + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + padding: 20, + backgroundColor: "#f5f5f5", + }, + title: { + fontSize: 28, + fontWeight: "bold", + marginBottom: 30, + textAlign: "center", + }, + input: { + height: 50, + borderColor: "#ccc", + borderWidth: 1, + borderRadius: 10, + paddingHorizontal: 15, + marginBottom: 15, + backgroundColor: "#fff", + }, + button: { + height: 50, + backgroundColor: "#4CAF50", + borderRadius: 10, + justifyContent: "center", + alignItems: "center", + marginTop: 10, + }, + buttonText: { + color: "#fff", + fontSize: 16, + fontWeight: "bold", + }, + link: { + marginTop: 15, + textAlign: "center", + color: "#007BFF", + }, +}); \ No newline at end of file diff --git a/apk/app/(tabs)/_layout.tsx b/apk/app/(tabs)/_layout.tsx new file mode 100644 index 0000000..5573dcd --- /dev/null +++ b/apk/app/(tabs)/_layout.tsx @@ -0,0 +1,13 @@ +// app/(tabs)/_layout.tsx +import { Redirect, Tabs } from "expo-router"; +import { useAuth } from "@/context/auth-context"; + +export default function TabsLayout() { + const { user } = useAuth(); + + if (!user) { + return ; + } + + return ; +} \ No newline at end of file diff --git a/apk/app/add.tsx b/apk/app/(tabs)/add.tsx similarity index 100% rename from apk/app/add.tsx rename to apk/app/(tabs)/add.tsx diff --git a/apk/app/index.tsx b/apk/app/(tabs)/index.tsx similarity index 100% rename from apk/app/index.tsx rename to apk/app/(tabs)/index.tsx diff --git a/apk/app/_layout.tsx b/apk/app/_layout.tsx index efab7b9..b42d716 100644 --- a/apk/app/_layout.tsx +++ b/apk/app/_layout.tsx @@ -1,31 +1,11 @@ -import { - DarkTheme, - DefaultTheme, - ThemeProvider, -} from "@react-navigation/native"; -import { Stack } from "expo-router"; -import { StatusBar } from "expo-status-bar"; -import "react-native-reanimated"; +import RootNavigation from "@/components/views/root-navigation"; +import { AuthProvider } from "@/context/auth-context"; -import { BirthdaysProvider } from "@/context/birthdays-context"; -import { useColorScheme } from "@/hooks/use-color-scheme"; - -export const unstable_settings = { - anchor: "(tabs)", -}; export default function RootLayout() { - const colorScheme = useColorScheme(); - return ( - - - - - - - - - + + + ); -} +} \ No newline at end of file diff --git a/apk/components/views/root-navigation.tsx b/apk/components/views/root-navigation.tsx new file mode 100644 index 0000000..89ae1b4 --- /dev/null +++ b/apk/components/views/root-navigation.tsx @@ -0,0 +1,9 @@ +import { useEffect } from "react"; +import { Stack, useRouter, useSegments } from "expo-router"; +import { useAuth } from "@/context/auth-context"; + +export default function RootLayout() { + return ( + + ); +} \ No newline at end of file diff --git a/apk/components/views/tabs.tsx b/apk/components/views/tabs.tsx new file mode 100644 index 0000000..efab7b9 --- /dev/null +++ b/apk/components/views/tabs.tsx @@ -0,0 +1,31 @@ +import { + DarkTheme, + DefaultTheme, + ThemeProvider, +} from "@react-navigation/native"; +import { Stack } from "expo-router"; +import { StatusBar } from "expo-status-bar"; +import "react-native-reanimated"; + +import { BirthdaysProvider } from "@/context/birthdays-context"; +import { useColorScheme } from "@/hooks/use-color-scheme"; + +export const unstable_settings = { + anchor: "(tabs)", +}; + +export default function RootLayout() { + const colorScheme = useColorScheme(); + + return ( + + + + + + + + + + ); +} diff --git a/apk/context/auth-context.tsx b/apk/context/auth-context.tsx new file mode 100644 index 0000000..66b0900 --- /dev/null +++ b/apk/context/auth-context.tsx @@ -0,0 +1,47 @@ +import AsyncStorage from "@react-native-async-storage/async-storage"; +import * as React from "react"; + +interface User { + email: string; +} + +interface AuthContextValue { + user: User | null; + login: (email: string, password: string) => Promise; + logout: () => void; +} + +const authContext = React.createContext(undefined); + +export function AuthProvider({ children }: { children: React.ReactNode }) { + const [user, setUser] = React.useState(null); + + const login = async (email: string, password: string) => { + const fakeUser = { email }; + + await AsyncStorage.setItem("user", JSON.stringify(fakeUser)); + setUser(fakeUser); + } + + const logout = async () => { + await AsyncStorage.removeItem("user"); + setUser(null); + } + + return ( + + {children} + + ); +} + +export function useAuth() { + const context = React.useContext(authContext); + if(!context) { + throw new Error("useAuth must be inside AuthProvider"); + } + return context; +} + + +