diff --git a/package.json b/package.json index c8b052b7..a7608e40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "betterseqtaplus", - "version": "3.5.0", + "version": "3.5.1", "type": "module", "description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development add add heaps more features!", "browserslist": "> 0.5%, last 2 versions, not dead", diff --git a/src/SEQTA.ts b/src/SEQTA.ts index d94c7b71..a646d64c 100644 --- a/src/SEQTA.ts +++ b/src/SEQTA.ts @@ -11,6 +11,30 @@ import { main } from "@/seqta/main"; import { delay } from "./seqta/utils/delay"; import { initializeHideSensitiveToggle } from "@/seqta/utils/hideSensitiveToggle"; +function registerFetchSeqtaAppLinkListener() { + browser.runtime.onMessage.addListener((request, _sender, sendResponse) => { + if (request?.type !== "fetchSeqtaAppLink") return false; + void (async () => { + try { + const res = await fetch(`${location.origin}/seqta/student/load/profile`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + credentials: "include", + body: JSON.stringify({}), + }); + const data = await res.json(); + const statusOk = data?.status === "200" || data?.status === 200; + const raw = data?.payload?.app_link; + const appLink = typeof raw === "string" && raw.length > 0 ? raw : null; + sendResponse({ appLink: statusOk ? appLink : null }); + } catch { + sendResponse({ appLink: null }); + } + })(); + return true; + }); +} + export let MenuOptionsOpen = false; var IsSEQTAPage = false; @@ -30,6 +54,8 @@ async function init() { IsSEQTAPage = true; console.info("[BetterSEQTA+] Verified SEQTA Page"); + registerFetchSeqtaAppLinkListener(); + const documentLoadStyle = document.createElement("style"); documentLoadStyle.textContent = documentLoadCSS; document.head.appendChild(documentLoadStyle); diff --git a/src/background.ts b/src/background.ts index dc23f115..0eae7596 100644 --- a/src/background.ts +++ b/src/background.ts @@ -167,8 +167,19 @@ function handleCloudFavorite(request: any, sendResponse: MessageSender): boolean return true; } -/** Handler for a message type; receives request and sendResponse callback */ -type MessageHandler = { (request: any, sendResponse: MessageSender): boolean | void }; +/** Handler for a message type; receives request, sendResponse, and optional sender (for tab routing) */ +type MessageHandler = { + (request: any, sendResponse: MessageSender, sender?: browser.Runtime.MessageSender): boolean | void; +}; + +function isSeqtaOrigin(origin: string): boolean { + try { + const u = new URL(origin); + return u.hostname.includes("seqta") || u.hostname.endsWith(".edu.au"); + } catch { + return false; + } +} const MESSAGE_HANDLERS: Record = { reloadTabs: () => reloadSeqtaPages(), @@ -200,29 +211,38 @@ const MESSAGE_HANDLERS: Record = { cloudLogin: handleCloudLogin, cloudRefresh: handleCloudRefresh, cloudFavorite: handleCloudFavorite, - getSeqtaSession: (req: { baseUrl?: string }, sendResponse: MessageSender) => { + getSeqtaSession: (req: { baseUrl?: string }, sendResponse: MessageSender, sender?: browser.Runtime.MessageSender) => { (async () => { try { - let baseUrl = req.baseUrl; - if (!baseUrl) { - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); + let tabId = sender?.tab?.id; + let originForCheck: string | undefined = req.baseUrl; + + if (tabId == null) { + const tabs = await browser.tabs.query({ active: true, lastFocusedWindow: true }); const tab = tabs[0]; - if (!tab?.url) { - sendResponse({ session: null }); + if (!tab?.id || !tab.url) { + sendResponse({ appLink: null }); return; } - baseUrl = new URL(tab.url).origin; + tabId = tab.id; + if (!originForCheck) originForCheck = new URL(tab.url).origin; + } else if (!originForCheck && sender?.tab?.url) { + originForCheck = new URL(sender.tab.url).origin; } - const cookies = await browser.cookies.getAll({ url: baseUrl }); - const jsession = cookies.find((c) => c.name === "JSESSIONID"); - if (jsession?.value) { - sendResponse({ session: { baseUrl, jsessionId: jsession.value } }); - } else { - sendResponse({ session: null }); + + if (!originForCheck || !isSeqtaOrigin(originForCheck)) { + sendResponse({ appLink: null }); + return; } + + const reply = (await browser.tabs.sendMessage(tabId, { type: "fetchSeqtaAppLink" })) as + | { appLink?: string | null } + | undefined; + const appLink = typeof reply?.appLink === "string" && reply.appLink.length > 0 ? reply.appLink : null; + sendResponse({ appLink }); } catch (err) { console.error("[Background] getSeqtaSession error:", err); - sendResponse({ session: null }); + sendResponse({ appLink: null }); } })(); return true; @@ -231,10 +251,10 @@ const MESSAGE_HANDLERS: Record = { browser.runtime.onMessage.addListener( // @ts-ignore - OnMessageListener expects literal true for async, we return boolean - (request: any, _: any, sendResponse: MessageSender) => { + (request: any, sender: browser.Runtime.MessageSender, sendResponse: MessageSender) => { const handler = MESSAGE_HANDLERS[request.type]; if (handler) { - const result = handler(request, sendResponse); + const result = handler(request, sendResponse, sender); return result === true; } console.log("Unknown request type"); diff --git a/src/interface/components/ConnectMobileApp.svelte b/src/interface/components/ConnectMobileApp.svelte index 544b7b99..345dd6be 100644 --- a/src/interface/components/ConnectMobileApp.svelte +++ b/src/interface/components/ConnectMobileApp.svelte @@ -4,11 +4,9 @@ import QRCode from "qrcode"; import { portal } from "../utils/portal"; - const DEEPLINK_PREFIX = "desqta://connect/"; - let showQrModal = $state(false); let qrDataUrl = $state(null); - let deeplink = $state(null); + let appLink = $state(null); let errorMessage = $state(null); let isLoading = $state(false); let isStandalone = $state(false); @@ -38,30 +36,21 @@ } } - function buildDesqtaConnectPayload(baseUrl: string, jsessionId: string): string { - const payload = JSON.stringify({ u: baseUrl, s: jsessionId }); - const base64 = btoa(unescape(encodeURIComponent(payload))); - const encoded = encodeURIComponent(base64); - return `${DEEPLINK_PREFIX}${encoded}`; - } - - async function getSession(): Promise<{ baseUrl: string; jsessionId: string } | null> { + async function getAppLink(): Promise { let baseUrl: string | undefined; if (isExtensionPage()) { - // Extension popup: background will get URL from active tab baseUrl = undefined; } else { - // In-page (settings inside SEQTA): pass current page URL (cookies API not available in content scripts) baseUrl = normalizeBaseUrl(window.location.href); if (!isSeqtaUrl(baseUrl)) return null; } - const { session } = (await browser.runtime.sendMessage({ + const { appLink: link } = (await browser.runtime.sendMessage({ type: "getSeqtaSession", baseUrl, - })) as { session: { baseUrl: string; jsessionId: string } | null }; - return session ?? null; + })) as { appLink: string | null }; + return link ?? null; } async function generateQrCode() { @@ -71,9 +60,9 @@ try { isStandalone = isExtensionPage(); - const session = await getSession(); + const link = await getAppLink(); - if (!session) { + if (!link) { if (isStandalone) { errorMessage = "Open SEQTA Learn in a tab and log in, then open settings from that tab to generate a QR code."; @@ -83,9 +72,8 @@ return; } - const link = buildDesqtaConnectPayload(session.baseUrl, session.jsessionId); const dataUrl = await QRCode.toDataURL(link, { width: 256, margin: 2 }); - deeplink = link; + appLink = link; qrDataUrl = dataUrl; showQrModal = true; } catch (err) { @@ -99,12 +87,12 @@ function closeModal() { showQrModal = false; qrDataUrl = null; - deeplink = null; + appLink = null; errorMessage = null; } - function openInDesqta() { - if (deeplink) window.location.href = deeplink; + function openAppLink() { + if (appLink) window.location.href = appLink; } function downloadQrImage() { @@ -160,12 +148,12 @@
- DesQTA QR Code + SEQTA Learn app link QR code
diff --git a/src/manifests/manifest.json b/src/manifests/manifest.json index de4f9b3a..7a3ba7e1 100644 --- a/src/manifests/manifest.json +++ b/src/manifests/manifest.json @@ -15,7 +15,7 @@ "64": "resources/icons/icon-64.png" } }, - "permissions": ["tabs", "notifications", "storage", "cookies"], + "permissions": ["tabs", "notifications", "storage"], "host_permissions": ["https://newsapi.org/", "https://betterseqta.org/", "https://accounts.betterseqta.org/", "*://*/*"], "background": { "service_worker": "background.ts" diff --git a/src/seqta/utils/Openers/OpenWhatsNewPopup.ts b/src/seqta/utils/Openers/OpenWhatsNewPopup.ts index bfbe195d..36cc54d9 100644 --- a/src/seqta/utils/Openers/OpenWhatsNewPopup.ts +++ b/src/seqta/utils/Openers/OpenWhatsNewPopup.ts @@ -32,6 +32,9 @@ export function OpenWhatsNewPopup() { const text = stringToHTML(/* html */ `
+

3.5.1 - QR & session link fix

+
  • Fixed DesQTA Connect Mobile App QR generation on Chrome
  • +

    3.5.0 - Adaptive Theme, Timetable Editor & More

  • Added adaptive theme colour
  • Added optional soft gradient for adaptive theme when viewing a class