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;
+}
+
+
+