diff --git a/.env.dev b/.env.dev
deleted file mode 100644
index e79a19c0..00000000
--- a/.env.dev
+++ /dev/null
@@ -1,3 +0,0 @@
-SUPABASE_DB_PASSWORD="AYH3i8hOkqZSY4Yx"
-SUPABASE_ANNON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imx4dXVyb3pxcnB0aHl2eHZuYW1sIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzkwNzg0NjYsImV4cCI6MjA1NDY1NDQ2Nn0.m8YcHx4RNlRRi4-GsnQTg5h_X_WfRlSY7iYKVmhZCTU"
-SUPABASE_URL="https://lxuurozqrpthyvxvnaml.supabase.co"
\ No newline at end of file
diff --git a/.env.example b/.env.example
deleted file mode 100644
index ce3c1262..00000000
--- a/.env.example
+++ /dev/null
@@ -1,3 +0,0 @@
-SUPABASE_DB_PASSWORD=
-SUPABASE_ANNON_KEY=
-SUPABASE_URL=
\ No newline at end of file
diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx
new file mode 100644
index 00000000..b8c5721b
--- /dev/null
+++ b/app/(auth)/_layout.tsx
@@ -0,0 +1,10 @@
+import { SafeAreaView } from "react-native";
+import { Slot } from "expo-router";
+
+export default function AuthLayout() {
+ return (
+
+
+
+ );
+}
diff --git a/app/(auth)/loginScreen.tsx b/app/(auth)/loginScreen.tsx
new file mode 100644
index 00000000..a5c7fdb9
--- /dev/null
+++ b/app/(auth)/loginScreen.tsx
@@ -0,0 +1,11 @@
+import { View } from "react-native";
+
+import { Login } from "@/fetaure/auth/screen/Login";
+
+export default function LoginScreen() {
+ return (
+
+
+
+ );
+}
diff --git a/app/(auth)/signupScreen.tsx b/app/(auth)/signupScreen.tsx
new file mode 100644
index 00000000..185dfef9
--- /dev/null
+++ b/app/(auth)/signupScreen.tsx
@@ -0,0 +1,11 @@
+import { View } from "react-native";
+
+import { Signup } from "@/fetaure/auth/screen/Signup";
+
+export default function SignupScreen() {
+ return (
+
+
+
+ );
+}
diff --git a/app/(protected)/_layout.tsx b/app/(protected)/_layout.tsx
new file mode 100644
index 00000000..53411a7c
--- /dev/null
+++ b/app/(protected)/_layout.tsx
@@ -0,0 +1,54 @@
+// app/(protected)/_layout.tsx
+import { useEffect, useState } from "react";
+import { ActivityIndicator, View } from "react-native";
+import { Slot, useRouter } from "expo-router";
+import { type User } from "@supabase/supabase-js";
+
+import supabase from "@/lib/supabase";
+
+export default function ProtectedLayout() {
+ const router = useRouter();
+ const [user, setUser] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ // 現在のセッションを取得
+ const getSession = async () => {
+ const {
+ data: { session },
+ } = await supabase.auth.getSession();
+ setUser(session?.user ?? null);
+ setLoading(false);
+ };
+ getSession();
+
+ // 認証状態の変化を監視
+ const {
+ data: { subscription },
+ } = supabase.auth.onAuthStateChange((_event, session) => {
+ setUser(session?.user ?? null);
+ });
+ return () => subscription?.unsubscribe();
+ }, []);
+
+ useEffect(() => {
+ if (!loading && !user) {
+ router.replace("/loginScreen");
+ }
+ }, [user, loading, router]);
+
+ if (loading) {
+ // ローディング中はインジケーターを表示
+ return (
+
+
+
+ );
+ }
+
+ if (!user) {
+ return null;
+ }
+
+ return ;
+}
diff --git a/app/index.tsx b/app/(protected)/index.tsx
similarity index 53%
rename from app/index.tsx
rename to app/(protected)/index.tsx
index 7b167a34..7b932301 100644
--- a/app/index.tsx
+++ b/app/(protected)/index.tsx
@@ -1,10 +1,12 @@
import { Text, View } from "react-native";
+import { SignoutButton } from "@/fetaure/auth/components/SignoutButton";
+
export default function Index() {
return (
-
- Hello World, Linpe
+
Hello World, Linpe
+
);
}
diff --git a/app/_layout.tsx b/app/_layout.tsx
index 3112b2bf..e0eb027a 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -1,7 +1,46 @@
-import { Stack } from "expo-router";
+// app/_layout.jsx
+import { SafeAreaView, Text } from "react-native";
+import { Slot, useRouter, useSegments } from "expo-router";
import "../assets/styles/global.css";
+import { useEffect, useState } from "react";
+import { type Session } from "@supabase/supabase-js";
+
+import supabase from "@/lib/supabase";
+
export default function RootLayout() {
- return ;
+ const [session, setSession] = useState(null);
+ const segments = useSegments();
+ const router = useRouter();
+
+ useEffect(() => {
+ supabase.auth.getSession().then(({ data: { session } }) => {
+ setSession(session);
+ });
+
+ supabase.auth.onAuthStateChange((_event, session) => {
+ setSession(session);
+ });
+ }, []);
+
+ useEffect(() => {
+ const inAuthGroup = segments[0] === "(auth)";
+ const inProtectedGroup = segments[0] === "(protected)";
+
+ if (session && inAuthGroup) {
+ // ログイン済みで認証画面にいる場合は、protectedにリダイレクト
+ router.replace("/(protected)");
+ } else if (!session && inProtectedGroup) {
+ // 未ログインでprotected画面にいる場合は、認証画面にリダイレクト
+ router.replace("/(auth)/loginScreen");
+ }
+ }, [session, segments, router]);
+
+ return (
+
+ {session ? "ログイン済み" : "未ログイン"}
+
+
+ );
}
diff --git a/components/button/PrimaryButton.tsx b/components/button/PrimaryButton.tsx
new file mode 100644
index 00000000..43dede7d
--- /dev/null
+++ b/components/button/PrimaryButton.tsx
@@ -0,0 +1,23 @@
+import { TouchableOpacity } from "react-native";
+
+export const PrimaryButton = ({
+ children,
+ onPress,
+ loading = false,
+}: {
+ children: React.ReactNode;
+ onPress: () => void;
+ loading?: boolean;
+}) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/components/link/DefaultLink.tsx b/components/link/DefaultLink.tsx
new file mode 100644
index 00000000..cdf9ba0c
--- /dev/null
+++ b/components/link/DefaultLink.tsx
@@ -0,0 +1,14 @@
+import { Link } from "expo-router";
+
+type DefaultLinkProps = Pick<
+ React.ComponentProps,
+ "children" | "href"
+>;
+
+export const DefaultLink = ({ children, href }: DefaultLinkProps) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/fetaure/auth/components/SignoutButton.tsx b/fetaure/auth/components/SignoutButton.tsx
new file mode 100644
index 00000000..1baf538a
--- /dev/null
+++ b/fetaure/auth/components/SignoutButton.tsx
@@ -0,0 +1,12 @@
+import { Text } from "react-native";
+
+import { PrimaryButton } from "@/components/button/PrimaryButton";
+import { signout } from "../service/authService";
+
+export const SignoutButton = () => {
+ return (
+ signout()}>
+ Signout
+
+ );
+};
diff --git a/fetaure/auth/screen/Login.tsx b/fetaure/auth/screen/Login.tsx
new file mode 100644
index 00000000..1c2b9b3d
--- /dev/null
+++ b/fetaure/auth/screen/Login.tsx
@@ -0,0 +1,51 @@
+import { useState } from "react";
+import { Text, TextInput, View } from "react-native";
+
+import { PrimaryButton } from "@/components/button/PrimaryButton";
+import { DefaultLink } from "@/components/link/DefaultLink";
+import { loginWithEmail } from "../service/authService";
+
+export const Login = () => {
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [loading, setLoading] = useState(false);
+
+ return (
+
+ Login
+
+ Email
+
+
+
+ Password
+
+
+ loginWithEmail(email, password, setLoading)}
+ loading={loading}
+ >
+ Login
+
+
+ Signup
+
+
+ );
+};
diff --git a/fetaure/auth/screen/Signup.tsx b/fetaure/auth/screen/Signup.tsx
new file mode 100644
index 00000000..a4010c77
--- /dev/null
+++ b/fetaure/auth/screen/Signup.tsx
@@ -0,0 +1,51 @@
+import { useState } from "react";
+import { Text, TextInput, View } from "react-native";
+
+import { PrimaryButton } from "@/components/button/PrimaryButton";
+import { DefaultLink } from "@/components/link/DefaultLink";
+import { signupWithEmail } from "../service/authService";
+
+export const Signup = () => {
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [loading, setLoading] = useState(false);
+
+ return (
+
+ Signup
+
+ Email
+
+
+
+ Password
+
+
+ signupWithEmail(email, password, setLoading)}
+ loading={loading}
+ >
+ Signup
+
+
+ Login
+
+
+ );
+};
diff --git a/fetaure/auth/service/authService.ts b/fetaure/auth/service/authService.ts
new file mode 100644
index 00000000..14a76f8d
--- /dev/null
+++ b/fetaure/auth/service/authService.ts
@@ -0,0 +1,42 @@
+import { Alert } from "react-native";
+
+import supabase from "@/lib/supabase";
+
+export const loginWithEmail = async (
+ email: string,
+ password: string,
+ setLoading: (loading: boolean) => void,
+) => {
+ setLoading(true);
+ const { error } = await supabase.auth.signInWithPassword({
+ email,
+ password,
+ });
+
+ if (error) Alert.alert(error.message);
+
+ setLoading(false);
+};
+
+export const signupWithEmail = async (
+ email: string,
+ password: string,
+ setLoading: (loading: boolean) => void,
+) => {
+ setLoading(true);
+ const {
+ data: { session },
+ error,
+ } = await supabase.auth.signUp({
+ email,
+ password,
+ });
+ if (error) Alert.alert(error.message);
+ if (!session) Alert.alert("Please check your inbox for email verification!");
+ setLoading(false);
+};
+
+export const signout = async () => {
+ const { error } = await supabase.auth.signOut();
+ if (error) Alert.alert(error.message);
+};
diff --git a/lib/supabase.ts b/lib/supabase.ts
index 86991d68..48eb6e46 100644
--- a/lib/supabase.ts
+++ b/lib/supabase.ts
@@ -62,8 +62,9 @@ class LargeSecureStore {
}
}
-const supabaseUrl = process.env.SUPABASE_URL;
-const supabaseAnonKey = process.env.SUPABASE_ANON_KEY;
+const supabaseUrl = "https://lxuurozqrpthyvxvnaml.supabase.co";
+const supabaseAnonKey =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imx4dXVyb3pxcnB0aHl2eHZuYW1sIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzkwNzg0NjYsImV4cCI6MjA1NDY1NDQ2Nn0.m8YcHx4RNlRRi4-GsnQTg5h_X_WfRlSY7iYKVmhZCTU";
const supabase = createClient(supabaseUrl || "", supabaseAnonKey || "", {
auth: {
diff --git a/tailwind.config.js b/tailwind.config.js
index 090d7c16..d8e8aca9 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,7 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
// NOTE: Update this to include the paths to all of your component files.
- content: ["./app/**/*.{js,jsx,ts,tsx}", "./app/*.{js,jsx,ts,tsx}"],
+ content: [
+ "./app/**/*.{js,jsx,ts,tsx}",
+ "./app/*.{js,jsx,ts,tsx}",
+ "./fetaure/**/*.{js,jsx,ts,tsx}",
+ ],
presets: [require("nativewind/preset")],
theme: {
extend: {},