mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-07-01 08:59:46 +02:00
140 lines
4.1 KiB
TypeScript
140 lines
4.1 KiB
TypeScript
'use client';
|
|
|
|
import { Loader2 } from 'lucide-react';
|
|
import React, { createContext, lazy, Suspense, useContext, useEffect, useMemo, useState } from 'react';
|
|
|
|
import { createAuthService, IAuthService, StackAuthService } from '../services';
|
|
import type { AuthUser } from '../types';
|
|
|
|
interface AuthContextType {
|
|
service: IAuthService;
|
|
user: AuthUser | null; // Union type: CurrentUser | LocalUser
|
|
isAuthenticated: boolean;
|
|
loading: boolean;
|
|
getAccessToken: () => Promise<string>;
|
|
provider: string;
|
|
}
|
|
|
|
const AuthContext = createContext<AuthContextType | null>(null);
|
|
|
|
interface AuthContextProviderProps {
|
|
service: IAuthService;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
// Lazy load Stack components only when needed
|
|
const StackProviderWrapper = lazy(() =>
|
|
import('./StackProviderWrapper').then(module => ({
|
|
default: module.StackProviderWrapper
|
|
}))
|
|
);
|
|
|
|
// Generic context provider for non-Stack providers
|
|
function GenericAuthContextProvider({ service, children }: AuthContextProviderProps) {
|
|
const [user, setUser] = useState<AuthUser | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
// Fetch current user
|
|
const fetchUser = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const currentUser = await service.getCurrentUser();
|
|
setUser(currentUser);
|
|
} catch (error) {
|
|
console.error('Failed to fetch user:', error);
|
|
setUser(null);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchUser();
|
|
}, [service]);
|
|
|
|
const getAccessToken = React.useCallback(() => service.getAccessToken(), [service]);
|
|
|
|
const contextValue: AuthContextType = React.useMemo(() => ({
|
|
service,
|
|
user,
|
|
isAuthenticated: service.isAuthenticated(),
|
|
loading,
|
|
getAccessToken,
|
|
provider: service.getProviderName(),
|
|
}), [service, user, loading, getAccessToken]);
|
|
|
|
return (
|
|
<AuthContext.Provider value={contextValue}>
|
|
{children}
|
|
</AuthContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|
const authProvider = process.env.NEXT_PUBLIC_AUTH_PROVIDER || 'stack';
|
|
const authService = useMemo(() => createAuthService(authProvider), [authProvider]);
|
|
|
|
// For Stack provider, wrap with StackProvider and use Stack-specific context
|
|
if (authProvider === 'stack' && authService instanceof StackAuthService) {
|
|
return (
|
|
<Suspense fallback={
|
|
<div className="flex items-center justify-center min-h-screen">
|
|
<Loader2 className="w-8 h-8 animate-spin text-gray-600" />
|
|
</div>
|
|
}>
|
|
<StackProviderWrapper service={authService}>
|
|
{children}
|
|
</StackProviderWrapper>
|
|
</Suspense>
|
|
);
|
|
}
|
|
|
|
// For other providers, use generic context provider
|
|
return (
|
|
<GenericAuthContextProvider service={authService}>
|
|
{children}
|
|
</GenericAuthContextProvider>
|
|
);
|
|
}
|
|
|
|
// Export the context for Stack-specific provider
|
|
export { AuthContext };
|
|
|
|
// Stack-specific context provider that uses the useUser hook
|
|
export function StackAuthContextProvider({ service, children }: AuthContextProviderProps) {
|
|
const [loading, setLoading] = useState(true);
|
|
const stackUser: AuthUser | null = null;
|
|
|
|
useEffect(() => {
|
|
// For Stack provider, we'll get the user from the StackProviderWrapper
|
|
// This is a placeholder that will be overridden by the actual implementation
|
|
if (service instanceof StackAuthService) {
|
|
setLoading(false);
|
|
}
|
|
}, [service]);
|
|
|
|
const getAccessToken = React.useCallback(() => service.getAccessToken(), [service]);
|
|
|
|
const contextValue: AuthContextType = React.useMemo(() => ({
|
|
service,
|
|
user: stackUser,
|
|
isAuthenticated: service.isAuthenticated(),
|
|
loading,
|
|
getAccessToken,
|
|
provider: service.getProviderName(),
|
|
}), [service, loading, getAccessToken]);
|
|
|
|
return (
|
|
<AuthContext.Provider value={contextValue}>
|
|
{children}
|
|
</AuthContext.Provider>
|
|
);
|
|
}
|
|
|
|
export function useAuthContext() {
|
|
const context = useContext(AuthContext);
|
|
if (!context) {
|
|
throw new Error('useAuthContext must be used within AuthProvider');
|
|
}
|
|
return context;
|
|
}
|