Skip to content

Commit 8618502

Browse files
committed
fix: log out if left inactive
1 parent debf023 commit 8618502

File tree

2 files changed

+156
-91
lines changed

2 files changed

+156
-91
lines changed

frontend/src/App.jsx

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,90 @@
1-
import { Routes, Route } from "react-router-dom";
1+
import { useEffect } from "react";
2+
import { Routes, Route, useNavigate } from "react-router-dom";
3+
import { onAuthStateChanged, signOut } from "firebase/auth";
4+
import { auth } from "../firebase";
5+
26
import Login from "./pages/login";
37
import Manage from "./pages/manage";
48
import DashboardPage from "./pages/dashboard";
5-
import ResponsePage from "./pages/responses"
9+
import ResponsePage from "./pages/responses";
610
import Admin from "./pages/rbac";
711
import Schedule from "./pages/scheduling";
812
import Create from "./pages/createSlot";
9-
import Domain from "./pages/domain"
13+
import Domain from "./pages/domain";
1014
import Sorry from "./pages/sorry";
1115
import Round2 from "./pages/round2";
1216

1317
export default function App() {
18+
const navigate = useNavigate();
19+
20+
const ACTIVITY_LIMIT = 20 * 60 * 1000;
21+
const LAST_ACTIVE_KEY = "lastActiveTime";
22+
23+
const updateLastActive = () => {
24+
localStorage.setItem(LAST_ACTIVE_KEY, Date.now().toString());
25+
};
26+
27+
const handleLogout = async () => {
28+
await signOut(auth);
29+
localStorage.removeItem(LAST_ACTIVE_KEY);
30+
navigate("/");
31+
};
32+
33+
const checkInactivity = async () => {
34+
const lastActive = localStorage.getItem(LAST_ACTIVE_KEY);
35+
if (!lastActive) return;
36+
37+
if (Date.now() - Number(lastActive) >= ACTIVITY_LIMIT) {
38+
await handleLogout();
39+
alert("You were logged out due to inactivity.");
40+
}
41+
};
42+
43+
useEffect(() => {
44+
const unsub = onAuthStateChanged(auth, async (user) => {
45+
if (user) {
46+
const lastActive = localStorage.getItem(LAST_ACTIVE_KEY);
47+
if (
48+
lastActive &&
49+
Date.now() - Number(lastActive) >= ACTIVITY_LIMIT
50+
) {
51+
await handleLogout();
52+
return;
53+
}
54+
updateLastActive();
55+
} else {
56+
localStorage.removeItem(LAST_ACTIVE_KEY);
57+
}
58+
});
59+
60+
return () => unsub();
61+
}, []);
62+
63+
useEffect(() => {
64+
const events = ["mousemove", "keydown", "click", "touchstart"];
65+
events.forEach((e) => window.addEventListener(e, updateLastActive));
66+
67+
const interval = setInterval(checkInactivity, 60000);
68+
69+
return () => {
70+
events.forEach((e) =>
71+
window.removeEventListener(e, updateLastActive)
72+
);
73+
clearInterval(interval);
74+
};
75+
}, []);
76+
1477
return (
1578
<Routes>
1679
<Route path="/" element={<Login />} />
1780
<Route path="/admin" element={<Admin />} />
1881
<Route path="/response" element={<ResponsePage />} />
1982
<Route path="/dashboard" element={<DashboardPage />} />
2083
<Route path="/schedule" element={<Schedule />} />
21-
<Route path="/create" element={<Create/>} />
22-
<Route path="/domain" element={<Domain/>} />
84+
<Route path="/create" element={<Create />} />
85+
<Route path="/domain" element={<Domain />} />
2386
<Route path="/manage" element={<Sorry />} />
87+
<Route path="/round2" element={<Round2 />} />
2488
</Routes>
2589
);
2690
}

frontend/src/pages/login.jsx

Lines changed: 87 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState, useRef } from "react";
1+
import { useEffect, useState } from "react";
22
import { signInWithPopup, onAuthStateChanged, signOut } from "firebase/auth";
33
import { auth, googleProvider } from "../../firebase";
44
import { useNavigate } from "react-router-dom";
@@ -7,63 +7,85 @@ export default function Login() {
77
const [loggedIn, setLoggedIn] = useState(false);
88
const [userInfo, setUserInfo] = useState({ name: "", email: "" });
99
const navigate = useNavigate();
10+
1011
const ACTIVITY_LIMIT = 20 * 60 * 1000;
11-
const inactivityTimer = useRef(null);
12+
const LAST_ACTIVE_KEY = "lastActiveTime";
1213

13-
const resetTimer = () => {
14-
if (!loggedIn) return;
15-
clearTimeout(inactivityTimer.current);
16-
inactivityTimer.current = setTimeout(
17-
() => handleLogout(true),
18-
ACTIVITY_LIMIT
19-
);
14+
const updateLastActive = () => {
15+
if (loggedIn) {
16+
localStorage.setItem(LAST_ACTIVE_KEY, Date.now().toString());
17+
}
2018
};
2119

22-
useEffect(() => {
23-
const events = ["mousemove", "keydown", "click", "touchstart"];
24-
events.forEach((e) => window.addEventListener(e, resetTimer));
25-
return () =>
26-
events.forEach((e) => window.removeEventListener(e, resetTimer));
27-
}, [loggedIn]);
20+
const handleLogout = async (auto) => {
21+
await signOut(auth);
22+
localStorage.removeItem(LAST_ACTIVE_KEY);
23+
setLoggedIn(false);
24+
if (auto) alert("You were logged out dude.");
25+
navigate("/");
26+
};
27+
28+
const checkInactivity = async () => {
29+
const lastActive = localStorage.getItem(LAST_ACTIVE_KEY);
30+
if (!lastActive) return;
31+
32+
if (Date.now() - Number(lastActive) >= ACTIVITY_LIMIT) {
33+
await handleLogout(true);
34+
}
35+
};
2836

2937
useEffect(() => {
30-
const unsub = onAuthStateChanged(auth, (user) => {
38+
const unsub = onAuthStateChanged(auth, async (user) => {
3139
if (user) {
40+
const lastActive = localStorage.getItem(LAST_ACTIVE_KEY);
41+
if (
42+
lastActive &&
43+
Date.now() - Number(lastActive) >= ACTIVITY_LIMIT
44+
) {
45+
await signOut(auth);
46+
localStorage.removeItem(LAST_ACTIVE_KEY);
47+
return;
48+
}
49+
3250
setUserInfo({
3351
name: user.displayName || "",
3452
email: user.email || ""
3553
});
3654
setLoggedIn(true);
37-
resetTimer();
55+
updateLastActive();
3856
} else {
3957
setLoggedIn(false);
4058
setUserInfo({ name: "", email: "" });
59+
localStorage.removeItem(LAST_ACTIVE_KEY);
4160
}
4261
});
62+
4363
return () => unsub();
4464
}, []);
4565

66+
useEffect(() => {
67+
if (!loggedIn) return;
68+
const interval = setInterval(checkInactivity, 60000);
69+
return () => clearInterval(interval);
70+
}, [loggedIn]);
71+
72+
useEffect(() => {
73+
const events = ["mousemove", "keydown", "click", "touchstart"];
74+
events.forEach((e) => window.addEventListener(e, updateLastActive));
75+
return () =>
76+
events.forEach((e) =>
77+
window.removeEventListener(e, updateLastActive)
78+
);
79+
}, [loggedIn]);
4680

4781
const loginWithGoogle = async () => {
48-
try {
49-
const result = await signInWithPopup(auth, googleProvider);
50-
setUserInfo({
51-
name: result.user.displayName,
52-
email: result.user.email
53-
});
54-
setLoggedIn(true);
55-
resetTimer();
56-
} catch (error) {
57-
console.error(error);
58-
}
59-
};
60-
61-
const handleLogout = async (auto) => {
62-
await signOut(auth);
63-
setLoggedIn(false);
64-
clearTimeout(inactivityTimer.current);
65-
if (auto) alert("You were logged out dude.");
66-
navigate("/");
82+
const result = await signInWithPopup(auth, googleProvider);
83+
setUserInfo({
84+
name: result.user.displayName,
85+
email: result.user.email
86+
});
87+
setLoggedIn(true);
88+
updateLastActive();
6789
};
6890

6991
const goToDashboard = () => navigate("/dashboard");
@@ -109,67 +131,46 @@ export default function Login() {
109131
height="28"
110132
viewBox="0 0 48 48"
111133
>
112-
<path
113-
fill="#FFC107"
114-
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
115-
/>
116-
<path
117-
fill="#FF3D00"
118-
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
119-
/>
120-
<path
121-
fill="#4CAF50"
122-
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
123-
/>
124-
<path
125-
fill="#1976D2"
126-
d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
127-
/>
134+
<path fill="#FFC107" d="M43.611,20.083H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20c0-1.341-0.138-2.65-0.389-3.917z"/>
135+
<path fill="#FF3D00" d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4c-7.682,0-14.344,4.337-17.694,10.691z"/>
136+
<path fill="#4CAF50" d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"/>
137+
<path fill="#1976D2" d="M43.611,20.083H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571l6.19,5.238C36.971,39.205,44,34,44,24c0-1.341-0.138-2.65-0.389-3.917z"/>
128138
</svg>
129-
<span className="text-base sm:text-lg">
130-
Continue with Google
131-
</span>
139+
<span className="text-base sm:text-lg">Continue with Google</span>
132140
</div>
133141
</button>
134142
)}
135143

136144
{loggedIn && (
137-
<div className="mt-7 bg-black/60 border border-yellow-700/30 p-4 rounded-2xl flex items-center gap-4">
138-
<div className="flex-shrink-0 w-10 h-10 sm:w-12 sm:h-12 rounded-full bg-yellow-400 flex items-center justify-center text-black font-bold">
139-
{userInfo.name
140-
? userInfo.name.charAt(0).toUpperCase()
141-
: "U"}
142-
</div>
143-
<div className="text-left">
144-
<div className="text-white font-semibold">
145-
{userInfo.name}
145+
<>
146+
<div className="mt-7 bg-black/60 border border-yellow-700/30 p-4 rounded-2xl flex items-center gap-4">
147+
<div className="flex-shrink-0 w-10 h-10 sm:w-12 sm:h-12 rounded-full bg-yellow-400 flex items-center justify-center text-black font-bold">
148+
{userInfo.name
149+
? userInfo.name.charAt(0).toUpperCase()
150+
: "U"}
146151
</div>
147-
<div className="text-xs sm:text-sm text-yellow-100/70 break-all">
148-
{userInfo.email}
152+
<div className="text-left">
153+
<div className="text-white font-semibold">{userInfo.name}</div>
154+
<div className="text-xs sm:text-sm text-yellow-100/70 break-all">
155+
{userInfo.email}
156+
</div>
149157
</div>
150158
</div>
151-
</div>
152-
)}
153159

154-
<button
155-
onClick={goToDashboard}
156-
disabled={!loggedIn}
157-
className={`mt-6 w-full py-3 rounded-xl text-base sm:text-lg font-semibold transition shadow-lg ${
158-
loggedIn
159-
? "bg-black/80 border border-yellow-400 text-white hover:bg-black/70"
160-
: "bg-neutral-800 text-neutral-400 cursor-not-allowed"
161-
}`}
162-
>
163-
Go to Dashboard
164-
</button>
160+
<button
161+
onClick={goToDashboard}
162+
className="mt-6 w-full py-3 rounded-xl text-base sm:text-lg font-semibold bg-black/80 border border-yellow-400 text-white hover:bg-black/70 transition shadow-lg"
163+
>
164+
Go to Dashboard
165+
</button>
165166

166-
{loggedIn && (
167-
<button
168-
onClick={() => handleLogout(false)}
169-
className="mt-4 w-full py-3 rounded-xl bg-red-600 hover:bg-red-700 text-white font-semibold text-base sm:text-lg shadow-md transition"
170-
>
171-
Logout
172-
</button>
167+
<button
168+
onClick={() => handleLogout(false)}
169+
className="mt-4 w-full py-3 rounded-xl bg-red-600 hover:bg-red-700 text-white font-semibold text-base sm:text-lg shadow-md transition"
170+
>
171+
Logout
172+
</button>
173+
</>
173174
)}
174175

175176
<div className="mt-6 text-xs sm:text-sm text-yellow-100/60">

0 commit comments

Comments
 (0)