Skip to content

Commit 21442eb

Browse files
samsinghhspotta85
andauthored
[DAP-90] unauthenticated/first-time user redirection in popup (#177)
* handle unauth redirection * unit test for login guidance * address code review comments * fix: separate popup authentication check * fix typo in comment * Minor Fixes --------- Co-authored-by: sidd03192 <siddharthpotta19@gmail.com>
1 parent 8902aa2 commit 21442eb

2 files changed

Lines changed: 54 additions & 15 deletions

File tree

features/audit-scraping/audit-history-sync.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@ import { parseAuditHistory } from "./audit-history-parser";
99
const AUDIT_HISTORY_URL =
1010
"https://utdirect.utexas.edu/apps/degree/audits/submissions/history/";
1111

12+
export async function isLoggedIn(): Promise<boolean> {
13+
try {
14+
const response = await fetch(AUDIT_HISTORY_URL, { credentials: "include" });
15+
return response.ok && !response.redirected;
16+
} catch {
17+
return false;
18+
}
19+
}
20+
1221
export async function fetchAuditHistory(): Promise<AuditHistoryEntry[]> {
1322
const response = await fetch(AUDIT_HISTORY_URL, { credentials: "include" });
1423
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

features/popup/popup-app.tsx

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,24 @@ import {
77
sendRuntimeMessage,
88
type ExtensionMessage,
99
} from "@/lib/browser/messages";
10-
import { PlusIcon, SpinnerIcon } from "@phosphor-icons/react";
10+
import { PlusIcon, SignInIcon, SpinnerIcon } from "@phosphor-icons/react";
1111
import React, { useCallback, useEffect, useState } from "react";
1212
import { browser } from "wxt/browser";
1313
import Button from "@/components/ui/button";
1414
import logo from "@/public/logo.png";
15+
import { isLoggedIn } from "@/features/audit-scraping/audit-history-sync";
1516
import PopupAuditCard from "./popup-audit-card";
1617

18+
const AUDIT_HOME_URL = "https://utdirect.utexas.edu/apps/degree/audits/";
19+
1720
export default function App() {
1821
const [audits, setAudits] = useState<AuditHistoryEntry[]>([]);
1922
const [loading, setLoading] = useState(true);
2023
const [error, setError] = useState<string | null>(null);
2124
const [showAll, setShowAll] = useState(false);
2225
const [runningAudit, setRunningAudit] = useState(false);
2326
const [isSyncing, setIsSyncing] = useState(false);
27+
const [loggedIn, setLoggedIn] = useState<boolean | null>(null);
2428

2529
const applyAuditHistory = useCallback((data: AuditHistoryData | null) => {
2630
if (data?.error) {
@@ -35,21 +39,19 @@ export default function App() {
3539
// Load audit history from cached storage
3640
// Storage is updated ONLY when user visits UT Direct audits home page
3741
// This allows popup to work from any page using cached data
38-
const refreshAudits = useCallback(async () => {
39-
try {
40-
applyAuditHistory(await getAuditHistory());
41-
} catch (e) {
42-
console.error("Error loading audit history:", e);
43-
setError("Failed to load audit history");
44-
}
42+
useEffect(() => {
43+
getAuditHistory()
44+
.then(applyAuditHistory)
45+
.catch(() => setError("Failed to load audit history"))
46+
.finally(() => setLoading(false));
4547
}, [applyAuditHistory]);
4648

4749
useEffect(() => {
48-
refreshAudits().finally(() => setLoading(false));
49-
}, [refreshAudits]);
50+
void isLoggedIn().then(setLoggedIn);
51+
}, []);
5052

5153
useEffect(() => {
52-
// get sycn status for ui
54+
// get sync status for ui
5355
sendRuntimeMessage({ type: "GET_SYNC_STATUS" })
5456
.then((response) => {
5557
if (response?.isSyncing) {
@@ -92,9 +94,19 @@ export default function App() {
9294
sendRuntimeMessage({ type: "RUN_NEW_AUDIT" });
9395
};
9496

97+
const handleLogin = () => {
98+
void browser.tabs.create({ url: AUDIT_HOME_URL, active: true });
99+
};
100+
95101
// Determine which audits to display
96102
const displayedAudits = showAll ? audits : audits.slice(0, 3);
97103
const hasMoreAudits = audits.length > 3;
104+
const needsLogin = loggedIn === false && audits.length === 0;
105+
// An authenticated empty history is a valid state, even if an older sync cached an error.
106+
const hasAuthenticatedEmptyHistory = loggedIn === true && audits.length === 0;
107+
// Only the empty state depends on auth (Login vs. empty); with cached audits we
108+
// can render immediately. So wait for auth ONLY when there's nothing to show yet.
109+
const resolvingLogin = loggedIn === null && audits.length === 0;
98110

99111
return (
100112
<div className="w-[438px] h-full min-h-[300px] max-h-[600px] bg-background font-sans overflow-hidden flex flex-col border border-gray-100">
@@ -113,8 +125,20 @@ export default function App() {
113125
</div>
114126

115127
<div className="flex items-center space-x-3">
116-
<Button className="rounded-md" onClick={handleRerunAudit}>
117-
{runningAudit ? (
128+
<Button
129+
className="rounded-md"
130+
onClick={needsLogin ? handleLogin : handleRerunAudit}
131+
>
132+
{resolvingLogin ? (
133+
<div className="flex items-center space-x-2">
134+
<SpinnerIcon size={24} className="animate-spin-slow" />
135+
</div>
136+
) : needsLogin ? (
137+
<div className="flex items-center space-x-2">
138+
<SignInIcon size={24} />
139+
<p className="text-lg font-bold">Login</p>
140+
</div>
141+
) : runningAudit ? (
118142
<div className="flex items-center space-x-2">
119143
<SpinnerIcon size={24} className="animate-spin-slow" />
120144
<p className="text-lg font-bold">Running Audit...</p>
@@ -125,6 +149,7 @@ export default function App() {
125149
<p className="text-lg font-bold">Run New Audit</p>
126150
</div>
127151
)}
152+
128153
</Button>
129154
</div>
130155
</header>
@@ -149,8 +174,13 @@ export default function App() {
149174

150175
{loading ? (
151176
<div className="flex flex-col gap-2 items-center justify-center text-center mb-6 py-8">
152-
<p className="text-base text-dap-gray-light">
153-
Loading audit history...
177+
<p className="text-base text-dap-gray-light">Syncing...</p>
178+
</div>
179+
) : needsLogin ? (
180+
<div className="flex flex-col gap-2 items-center justify-center text-center mb-6 py-8">
181+
<p className="text-base text-dap-gray-light tracking-[0.32px] max-w-[300px]">
182+
Log in to UT Direct, then visit the Degree Audit page to load your
183+
audits.
154184
</p>
155185
</div>
156186
) : error ? (

0 commit comments

Comments
 (0)