login time
This commit is contained in:
+52
-32
@@ -4,6 +4,7 @@ import {
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
useRef,
|
||||
} from "react";
|
||||
import * as WebBrowser from "expo-web-browser";
|
||||
|
||||
@@ -47,6 +48,8 @@ export const AuthProvider = ({ children }: AuthProviderProps) => {
|
||||
const [tokenState, setTokenState] = useState<string | null>(getToken());
|
||||
const [bootstrapping, setBootstrapping] = useState(true);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const loginInFlightRef = useRef(false);
|
||||
const loginPromiseRef = useRef<Promise<void> | null>(null);
|
||||
|
||||
const isAuthenticated = !!tokenState && !!user;
|
||||
|
||||
@@ -79,39 +82,56 @@ export const AuthProvider = ({ children }: AuthProviderProps) => {
|
||||
}, []);
|
||||
|
||||
const login: AuthContextType["login"] = async (email, password) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await fetch(`${getApiUrl()?.replace(/\/$/, "")}/auth/login`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const status = res.status;
|
||||
let msg = "Invalid credentials";
|
||||
try {
|
||||
const errData = await res.json();
|
||||
msg = errData?.message || errData?.error || msg;
|
||||
} catch {
|
||||
// non-JSON response, keep default msg
|
||||
}
|
||||
// Only replace the message for explicit 'not activated' cases
|
||||
if (status === 403 && /not activated/i.test(msg)) {
|
||||
msg =
|
||||
"Account is not activated. Please use your invitation link to set a password.";
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
const data = await res.json();
|
||||
await setToken(data.token);
|
||||
setTokenState(data.token);
|
||||
setUser(data.user);
|
||||
// Clear/refresh caches after login for correct user context
|
||||
store.dispatch(baseApi.util.resetApiState());
|
||||
store.dispatch(baseApi.util.invalidateTags(["Users", "Todos"]));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
if (loginInFlightRef.current && loginPromiseRef.current) {
|
||||
return await loginPromiseRef.current;
|
||||
}
|
||||
|
||||
loginInFlightRef.current = true;
|
||||
setLoading(true);
|
||||
|
||||
const p = (async () => {
|
||||
try {
|
||||
const res = await fetch(`${getApiUrl()?.replace(/\/$/, "")}/auth/login`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const status = res.status;
|
||||
let msg = "Invalid credentials";
|
||||
let serverMsg: string | undefined;
|
||||
try {
|
||||
const errData = await res.json();
|
||||
serverMsg = errData?.message || errData?.error;
|
||||
} catch {}
|
||||
|
||||
if (status === 403 && serverMsg && /not activated/i.test(serverMsg)) {
|
||||
msg =
|
||||
"Account is not activated. Please use your invitation link to set a password.";
|
||||
} else if (status >= 400 && status < 500) {
|
||||
msg = serverMsg || msg;
|
||||
} else {
|
||||
// For 5xx, keep default 'Invalid credentials' to avoid leaking internals
|
||||
msg = "Invalid credentials";
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
const data = await res.json();
|
||||
await setToken(data.token);
|
||||
setTokenState(data.token);
|
||||
setUser(data.user);
|
||||
// Clear/refresh caches after login for correct user context
|
||||
store.dispatch(baseApi.util.resetApiState());
|
||||
store.dispatch(baseApi.util.invalidateTags(["Users", "Todos"]));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
loginInFlightRef.current = false;
|
||||
loginPromiseRef.current = null;
|
||||
}
|
||||
})();
|
||||
|
||||
loginPromiseRef.current = p;
|
||||
return await p;
|
||||
};
|
||||
|
||||
const register: AuthContextType["register"] = async ({
|
||||
|
||||
Reference in New Issue
Block a user