-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAuthContextProvider.tsx
More file actions
150 lines (133 loc) · 3.9 KB
/
AuthContextProvider.tsx
File metadata and controls
150 lines (133 loc) · 3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright (c) Gridiron Survivor.
// Licensed under the MIT License.
'use client';
import React, { JSX, useEffect } from 'react';
import { createContext, useContext, useMemo, useState } from 'react';
import { account } from '@/api/config';
import { useRouter } from 'next/navigation';
import { useDataStore } from '@/store/dataStore';
import type { DataStore } from '@/store/dataStore';
import { IUser } from '@/api/apiFunctions.interface';
import { getCurrentUser } from '@/api/apiFunctions';
type UserCredentials = {
email: string;
password: string;
};
type AuthContextType = {
isSignedIn: boolean;
setIsSignedIn: React.Dispatch<React.SetStateAction<boolean>>;
loginAccount: (user: UserCredentials) => Promise<void | Error>; // eslint-disable-line no-unused-vars
logoutAccount: () => Promise<void>;
getUser: () => Promise<void | IUser>;
};
export const AuthContext = createContext<AuthContextType | null>(null);
/**
* Provider for the authentication context.
* @param children - The children to render.
* @param children.children - The children to render.
* @returns The rendered provider.
*/
export const AuthContextProvider = ({
children,
}: {
children: React.ReactNode;
}): JSX.Element => {
const [isSignedIn, setIsSignedIn] = useState<boolean>(false);
const { updateUser, resetUser, user } = useDataStore<DataStore>(
(state) => state,
);
const router = useRouter();
/**
* Authenticate and set session state
* @param user - The user credentials.
* @returns The error if there is one.
*/
const loginAccount = async (user: UserCredentials): Promise<void | Error> => {
try {
await account.createEmailPasswordSession(user.email, user.password);
router.push('/league/all');
} catch (error) {
console.error('Login error:', error);
return error as Error;
}
};
/**
* Log out and clear session state
* @returns {Promise<void>}
*/
const logoutAccount = async (): Promise<void> => {
try {
await account.deleteSession('current');
setIsSignedIn(false);
resetUser(); // Reset user data in the store
router.push('/login');
} catch (error) {
console.error('Logout error:', error);
}
};
/**
* Get user data from the session
* @returns {Promise<void | IUser>} - The user data or an error.
*/
const getUser = async (): Promise<void | IUser> => {
if (!isSessionInLocalStorage()) {
router.push('/login');
return;
}
try {
const user = await account.get();
const userData: IUser = await getCurrentUser(user.$id);
updateUser(userData.id, userData.email, userData.leagues);
return userData;
} catch (error) {
resetUser();
setIsSignedIn(false);
}
};
/**
* Helper function to validate session data in local storage
* @returns {boolean} - Whether the session is in local storage.
*/
const isSessionInLocalStorage = (): boolean => {
const loadedDataString = localStorage.getItem('cookieFallback');
if (!loadedDataString || loadedDataString === '[]') {
localStorage.removeItem('cookieFallback');
return false;
}
return true;
};
useEffect(() => {
if (!user.id || user.id === '') {
getUser();
return;
}
}, [user]);
// Memoize context values to avoid unnecessary re-renders
const contextValue = useMemo(
() => ({
isSignedIn,
setIsSignedIn,
loginAccount,
logoutAccount,
getUser,
}),
[isSignedIn],
);
return (
<AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>
);
};
/**
* Custom hook to access the authentication context
* @returns The authentication context.
*/
const useAuthContext = (): AuthContextType => {
const context = useContext(AuthContext);
if (!context) {
throw new Error(
'useAuthContext must be used within an AuthContextProvider',
);
}
return context;
};
export { useAuthContext };