Fix Android documents and idle session

This commit is contained in:
2026-04-23 10:12:00 -05:00
parent 9e0fbcaa0d
commit 622048b163
11 changed files with 168 additions and 104 deletions
+1
View File
@@ -6,6 +6,7 @@ export const register = (data) => client.post('/auth/register', data, {
},
});
export const login = (data) => client.post('/auth/login', data);
export const refreshSession = () => client.post('/auth/refresh', {});
export const logout = () => client.post('/auth/logout', {});
export const getMe = () => client.get('/auth/me');
export const verifyEmail = (token) => client.get(`/auth/verify/${token}`);
+46 -18
View File
@@ -1,31 +1,24 @@
import { createContext, useContext, useState, useEffect, useCallback } from 'react';
import { login as apiLogin, logout as apiLogout, getMe } from '../api/auth';
import { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';
import { login as apiLogin, logout as apiLogout, getMe, refreshSession as apiRefreshSession } from '../api/auth';
import { updateSignatureAppearancePreference as apiUpdateSignatureAppearancePreference } from '../api/users';
import {
clearAccessToken,
expireSession,
getAccessToken,
getAccessTokenExpiryDelay,
SESSION_EXPIRED_EVENT,
setAccessToken,
} from '../utils/session';
const AuthContext = createContext(null);
const IDLE_SESSION_MS = 15 * 60 * 1000;
const ACTIVE_REFRESH_MS = 9 * 60 * 1000;
const ACTIVITY_EVENTS = ['click', 'keydown', 'mousemove', 'scroll', 'touchstart'];
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const scheduleSessionExpiry = useCallback((token) => {
const delay = getAccessTokenExpiryDelay(token);
if (delay === null) return undefined;
const timeoutId = window.setTimeout(() => {
expireSession();
}, delay);
return () => window.clearTimeout(timeoutId);
}, []);
const lastActivityRef = useRef(Date.now());
const lastRefreshRef = useRef(0);
useEffect(() => {
const token = getAccessToken();
@@ -48,15 +41,50 @@ export function AuthProvider({ children }) {
}, []);
useEffect(() => {
const token = getAccessToken();
if (!user || !token) return undefined;
return scheduleSessionExpiry(token);
}, [scheduleSessionExpiry, user]);
if (!user) return undefined;
const registerActivity = () => {
const now = Date.now();
if (now - lastActivityRef.current > IDLE_SESSION_MS) {
expireSession();
return;
}
lastActivityRef.current = now;
};
const keepAlive = async () => {
const now = Date.now();
if (now - lastActivityRef.current > IDLE_SESSION_MS) {
expireSession();
return;
}
if (now - lastRefreshRef.current < ACTIVE_REFRESH_MS || !getAccessToken()) return;
try {
const { data } = await apiRefreshSession();
setAccessToken(data.data.accessToken);
lastRefreshRef.current = now;
} catch {
expireSession();
}
};
ACTIVITY_EVENTS.forEach((eventName) => window.addEventListener(eventName, registerActivity, { passive: true }));
const intervalId = window.setInterval(keepAlive, 60 * 1000);
return () => {
ACTIVITY_EVENTS.forEach((eventName) => window.removeEventListener(eventName, registerActivity));
window.clearInterval(intervalId);
};
}, [user]);
const login = useCallback(async (email, password) => {
const { data } = await apiLogin({ email, password });
const { accessToken, user: userData } = data.data;
setAccessToken(accessToken);
lastActivityRef.current = Date.now();
lastRefreshRef.current = Date.now();
setUser(userData);
return userData;
}, []);