xtablo-source/xtablo-expo/stores/auth.tsx
Arthur Belleville 75328fbe96
Format codebase
2025-10-10 08:50:56 +02:00

185 lines
5 KiB
TypeScript

import { create } from "zustand";
import { Provider, Session } from "@supabase/supabase-js";
import { supabase } from "@/lib/supabase";
import * as WebBrowser from "expo-web-browser";
import { makeRedirectUri } from "expo-auth-session";
import * as QueryParams from "expo-auth-session/build/QueryParams";
import { Linking } from "react-native";
import { QueryClient } from "@tanstack/react-query";
import { User } from "@/types/user.types";
import { api } from "@/lib/api";
import * as AppleAuthentication from "expo-apple-authentication";
interface AuthState {
session: Session | null;
user: User | null;
loading: boolean;
initialized: boolean;
initialize: (queryClient: QueryClient) => Promise<void>;
setSession: (session: Session | null) => void;
login: (email: string, password: string) => Promise<void>;
signUp: (
email: string,
password: string,
firstName: string,
lastName: string,
companyName: string
) => Promise<void>;
performOAuth: (provider: Provider) => Promise<void>;
signInWithApple: () => Promise<void>;
signOut: () => Promise<void>;
createSessionFromUrl: (url: string) => Promise<void>;
fetchAndSetUser: (session: Session | null) => Promise<void>;
}
WebBrowser.maybeCompleteAuthSession();
const redirectTo = makeRedirectUri({ path: "/(app)/(tabs)" });
export const useAuthStore = create<AuthState>((set, get) => ({
user: null,
session: null,
loading: true,
initialized: false,
setSession: (session: Session | null) => set({ session }),
fetchAndSetUser: async (session: Session | null) => {
if (!session) return;
try {
const { data } = await api.get<User>("/api/v1/users/me", {
headers: {
Authorization: `Bearer ${session?.access_token}`,
},
});
set({ user: data });
} catch (error) {
console.error("Error fetching user:", error);
}
},
initialize: async (queryClient: QueryClient) => {
try {
const {
data: { session },
} = await supabase.auth.getSession();
set({
session,
});
supabase.auth.onAuthStateChange(async (event, session) => {
set({
session,
});
await get().fetchAndSetUser(session);
});
const initialUrl = await Linking.getInitialURL();
if (initialUrl) {
await get().createSessionFromUrl(initialUrl);
}
Linking.addEventListener("url", ({ url }) => {
get().createSessionFromUrl(url);
});
} catch (error) {
console.error("Auth initialization error:", error);
} finally {
set({ loading: false });
set({ initialized: true });
queryClient.invalidateQueries({ queryKey: ["user"] });
}
},
login: async (email: string, password: string) => {
const { error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) throw error;
set({ loading: false });
},
signUp: async (
email: string,
password: string,
firstName: string,
lastName: string,
companyName: string
) => {
await supabase.auth.signUp({
email,
password,
options: {
data: {
firstName,
lastName,
companyName,
},
},
});
set({ loading: false });
},
performOAuth: async (provider: Provider) => {
const { data, error } = await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo,
skipBrowserRedirect: true,
queryParams: {
access_type: "offline",
prompt: "consent",
},
},
});
if (error) throw error;
const res = await WebBrowser.openAuthSessionAsync(data?.url ?? "", redirectTo);
if (res.type === "success") {
const { url } = res;
await get().createSessionFromUrl(url);
}
},
signInWithApple: async () => {
try {
const credential = await AppleAuthentication.signInAsync({
requestedScopes: [
AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
AppleAuthentication.AppleAuthenticationScope.EMAIL,
],
});
if (credential.identityToken) {
const {
error,
data: { session },
} = await supabase.auth.signInWithIdToken({
provider: "apple",
token: credential.identityToken,
});
if (!error && session) {
await set({ session });
}
} else {
throw new Error("No identityToken.");
}
} catch (e) {
console.error("Error signing in with Apple:", e);
}
},
signOut: async () => {
await supabase.auth.signOut();
set({ loading: false, user: null, session: null });
},
createSessionFromUrl: async (url: string) => {
const { params, errorCode } = QueryParams.getQueryParams(url);
if (errorCode) throw new Error(errorCode);
const { access_token, refresh_token } = params;
if (!access_token) return;
const { data, error } = await supabase.auth.setSession({
access_token,
refresh_token,
});
if (error) throw error;
set({ session: data.session });
},
}));