Debug when user stays online at closing browser

This commit is contained in:
Alex Erdei 2025-05-07 22:46:45 +01:00
parent b2affa91c7
commit 545413d2ce
2 changed files with 172 additions and 132 deletions

View File

@ -153,34 +153,22 @@ const openSocket = () => {
}); });
}; };
// auth helpers -----------------------------------------------------------
const setAuth = (t, u) => {
authToken = t;
authUser = u;
saveAuthToStorage(t, u);
openSocket();
};
const clearAuth = () => {
authToken = null;
authUser = null;
clearAuthStorage();
if (hub) {
hub.stop();
hub = null;
}
};
// presence --------------------------------------------------------------- // presence ---------------------------------------------------------------
const patchOnline = async (on) => { // patchOnline(on, keep = false) ← keep=true will add keepalive: true
const patchOnline = async (on, keep = false) => {
if (!authUser) return; if (!authUser) return;
try { try {
await $fetch(USERS_URL, { await fetch(USERS_URL, {
method: "PUT", method: "PUT",
headers: { "Content-Type": "application/json", ...authHeader() },
body: JSON.stringify({ user_id: authUser, isOnline: on ? 1 : 0 }), body: JSON.stringify({ user_id: authUser, isOnline: on ? 1 : 0 }),
...(keep ? { keepalive: true } : {}),
}); });
} catch (e) { } catch (e) {
console.warn("[online] PUT failed:", e.message); console.warn("[online] PUT failed:", e.message);
@ -191,6 +179,77 @@ export const currentUserOnline = () => patchOnline(true);
export const currentUserOffline = () => patchOnline(false); export const currentUserOffline = () => patchOnline(false);
// leave / background handlers -------------------------------------------
let leaveHandlerInstalled = false;
const sendOfflineKeepalive = () => patchOnline(false, true);
const handleVisibility = () => {
if (document.visibilityState === "hidden") sendOfflineKeepalive();
if (document.visibilityState === "visible") currentUserOnline();
};
const installLeaveHandlers = () => {
if (leaveHandlerInstalled) return;
window.addEventListener("pagehide", sendOfflineKeepalive); // tab / window close
window.addEventListener("freeze", sendOfflineKeepalive); // page-lifecycle freeze
window.addEventListener("resume", currentUserOnline); // page-lifecycle resume
document.addEventListener("visibilitychange", handleVisibility);
leaveHandlerInstalled = true;
};
const removeLeaveHandlers = () => {
if (!leaveHandlerInstalled) return;
window.removeEventListener("pagehide", sendOfflineKeepalive);
window.removeEventListener("freeze", sendOfflineKeepalive);
window.removeEventListener("resume", currentUserOnline);
document.removeEventListener("visibilitychange", handleVisibility);
leaveHandlerInstalled = false;
};
// setAuth … add
const setAuth = (t, u) => {
authToken = t;
authUser = u;
saveAuthToStorage(t, u);
openSocket();
installLeaveHandlers(); // ← here
};
// clearAuth … add
const clearAuth = () => {
authToken = null;
authUser = null;
clearAuthStorage();
if (hub) {
hub.stop();
hub = null;
}
removeLeaveHandlers(); // ← here
};
// bootstrap after login/restore ----------------------------------------- // bootstrap after login/restore -----------------------------------------
const bootstrapSession = async (uid) => { const bootstrapSession = async (uid) => {

View File

@ -86,7 +86,7 @@ const UserAccount = () => {
/* -------------------------------------------------- */ /* -------------------------------------------------- */
/* Firestore-like subscriptions & online/offline flag */ /* Firestore-like subscriptions */
/* -------------------------------------------------- */ /* -------------------------------------------------- */
@ -101,21 +101,6 @@ const UserAccount = () => {
currentUserOnline(); currentUserOnline();
/* window closed or refreshed */
const beforeUnload = () => currentUserOffline();
window.addEventListener("beforeunload", beforeUnload);
/* tab visibility switch */
const visChange = () =>
document.visibilityState === "visible"
? currentUserOnline()
: currentUserOffline();
document.addEventListener("visibilitychange", visChange);
/* cleanup */ /* cleanup */
return () => { return () => {
@ -124,10 +109,6 @@ const UserAccount = () => {
unsubUsers(); unsubUsers();
unsubPosts(); unsubPosts();
window.removeEventListener("beforeunload", beforeUnload);
document.removeEventListener("visibilitychange", visChange);
}; };
}, []); }, []);
@ -165,8 +146,8 @@ const UserAccount = () => {
/* -------------------------------------------------- */ /* -------------------------------------------------- */
return ( return (
<div className='bg-200 vw-100 main-container overflow-hidden'> <div className="bg-200 vw-100 main-container overflow-hidden">
<Container className='w-100 p-0' fluid> <Container className="w-100 p-0" fluid>
<Router> <Router>
<RouteStateSync /> <RouteStateSync />
@ -175,29 +156,29 @@ const UserAccount = () => {
<Switch> <Switch>
{/* Friends list ------------------------------------------------ */} {/* Friends list ------------------------------------------------ */}
<Route path='/fakebook/friends/list' component={FriendsListPage} /> <Route path="/fakebook/friends/list" component={FriendsListPage} />
{/* Single photo ----------------------------------------------- */} {/* Single photo ----------------------------------------------- */}
<Route path='/fakebook/photo/:userID/:n' component={PhotoViewer} /> <Route path="/fakebook/photo/:userID/:n" component={PhotoViewer} />
{/* Watch (video feed) ----------------------------------------- */} {/* Watch (video feed) ----------------------------------------- */}
<Route <Route
path='/fakebook/watch' path="/fakebook/watch"
render={(props) => <HomePage {...props} className='pt-5' />} render={(props) => <HomePage {...props} className="pt-5" />}
/> />
{/* User profile ----------------------------------------------- */} {/* User profile ----------------------------------------------- */}
<Route path='/fakebook/:userName' component={Profile} /> <Route path="/fakebook/:userName" component={Profile} />
{/* News-feed root --------------------------------------------- */} {/* News-feed root --------------------------------------------- */}
<Route <Route
path='/fakebook' path="/fakebook"
exact exact
render={(props) => <HomePage {...props} className='pt-5' />} render={(props) => <HomePage {...props} className="pt-5" />}
/> />
</Switch> </Switch>
</Router> </Router>