diff --git a/docs/CLOUD_SETTINGS_SYNC_SERVER.md b/docs/CLOUD_SETTINGS_SYNC_SERVER.md index d9b502e8..2a57d8cd 100644 --- a/docs/CLOUD_SETTINGS_SYNC_SERVER.md +++ b/docs/CLOUD_SETTINGS_SYNC_SERVER.md @@ -120,6 +120,7 @@ The backup is a flat JSON map of **`chrome.storage.local`** keys. It does **not* - **OAuth / session keys** — `bsplus_token`, `bsplus_refresh_token`, `bsplus_client_id`, `bsplus_user`, plus legacy `cloudAccessToken` / `cloudUsername`. - **Assessment Averages caches** — `plugin.assessments-average.storage.assessments`, `plugin.assessments-average.storage.weightings` (school assessment data). - **Keys under** `plugin.global-search.storage.*` — reserved so any future plugin storage cache there is not synced. +- **Grade Analytics** — keys under `bsplus.analytics.*` (synced assessment cache and per-school chart preferences). - **`bsplus_cloud_settings_known_remote_updated_at`** — client-only watermark for auto-sync (not part of the cloud backup blob). On restore, those keys are **not** taken from the server; the device keeps its current local values. diff --git a/package.json b/package.json index 8f0615e0..1c53c20b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "betterseqtaplus", - "version": "3.6.6", + "version": "3.7.0", "type": "module", "description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development and add heaps more features!", "browserslist": "> 0.5%, last 2 versions, not dead", diff --git a/src/css/injected.scss b/src/css/injected.scss index a1580330..978cd32b 100644 --- a/src/css/injected.scss +++ b/src/css/injected.scss @@ -3558,6 +3558,32 @@ div.day-empty { color: var(--text-primary); transform-origin: center center; } + +/* Text-only popups (privacy notices): body fills remaining height, scrolls inside */ +.whatsnewContainer.whatsnewContainer--scrollBody { + .whatsnewHeader { + flex-shrink: 0; + height: auto; + min-height: 3em; + } + + > .whatsnewTextContainer { + flex: 1 1 auto; + min-height: 0; + overflow-x: hidden; + overflow-y: auto; + max-height: none; + width: 90%; + margin: 0 auto 0.75rem; + padding-bottom: 0.5rem; + box-sizing: border-box; + } + + > .whatsnewTextContainer.privacyStatement { + font-size: 1.1rem; + line-height: 1.6; + } +} .whatsnewTextContainer.privacyStatement p { margin-bottom: 1.5ex; diff --git a/src/plugins/built-in/gradeAnalytics/GradeAnalyticsPage.svelte b/src/plugins/built-in/gradeAnalytics/GradeAnalyticsPage.svelte index 43eab2d3..e2afc141 100644 --- a/src/plugins/built-in/gradeAnalytics/GradeAnalyticsPage.svelte +++ b/src/plugins/built-in/gradeAnalytics/GradeAnalyticsPage.svelte @@ -16,6 +16,7 @@ TIME_RANGE_OPTIONS, type TimeRange, } from "./timeRange"; + import { openAnalyticsPrivacyPopup } from "./openAnalyticsPrivacyPopup"; let analyticsData: Assessment[] | null = $state(null); let loading = $state(true); @@ -208,14 +209,23 @@

Last updated: {formattedTimestamp()}

{/if} - +
+ + +
{#if error} diff --git a/src/plugins/built-in/gradeAnalytics/core/index.ts b/src/plugins/built-in/gradeAnalytics/core/index.ts index 9c9555cf..16d36394 100644 --- a/src/plugins/built-in/gradeAnalytics/core/index.ts +++ b/src/plugins/built-in/gradeAnalytics/core/index.ts @@ -1,9 +1,13 @@ import type { Plugin } from "@/plugins/core/types"; +import MenuitemSVGKey from "@/seqta/content/MenuItemSVGKey.json"; import { waitForElm } from "@/seqta/utils/waitForElm"; import { isSeqtaEngageExperience } from "@/seqta/utils/isSeqtaEngage"; +import { processMenuItemNode } from "@/seqta/utils/sidebarMenuIcons"; import { loadAnalyticsPage } from "../loadAnalyticsPage"; import styles from "../styles.css?inline"; +const ANALYTICS_MENU_ICON = MenuitemSVGKey.analytics; + const ANALYTICS_MENU_CLASS = "betterseqta-grade-analytics-item"; const gradeAnalyticsPlugin: Plugin<{}> = { @@ -30,7 +34,7 @@ const gradeAnalyticsPlugin: Plugin<{}> = { analyticsItem.dataset.key = "analytics"; analyticsItem.dataset.path = "/analytics"; analyticsItem.dataset.betterseqta = "true"; - analyticsItem.innerHTML = ``; + analyticsItem.innerHTML = ``; const homeButton = document.getElementById("homebutton"); if (homeButton?.parentElement === menuList) { @@ -39,6 +43,8 @@ const gradeAnalyticsPlugin: Plugin<{}> = { menuList.insertBefore(analyticsItem, menuList.firstChild); } + processMenuItemNode(analyticsItem); + const menuObserver = new MutationObserver(() => { if (!menuList.contains(analyticsItem)) { if (homeButton?.parentElement === menuList) { @@ -46,6 +52,7 @@ const gradeAnalyticsPlugin: Plugin<{}> = { } else { menuList.insertBefore(analyticsItem, menuList.firstChild); } + processMenuItemNode(analyticsItem); } }); menuObserver.observe(menuList, { childList: true }); diff --git a/src/plugins/built-in/gradeAnalytics/openAnalyticsPrivacyPopup.ts b/src/plugins/built-in/gradeAnalytics/openAnalyticsPrivacyPopup.ts new file mode 100644 index 00000000..4f14f4ca --- /dev/null +++ b/src/plugins/built-in/gradeAnalytics/openAnalyticsPrivacyPopup.ts @@ -0,0 +1,61 @@ +import stringToHTML from "@/seqta/utils/stringToHTML"; +import { openPopup } from "@/seqta/utils/Openers/PopupManager"; + +/** Grade Analytics privacy — uses the shared BetterSEQTA+ whatsnew popup shell. */ +export function openAnalyticsPrivacyPopup() { + const header = stringToHTML( + /* html */ + `
+

Privacy notice

+

Grade Analytics on this device

+
`, + ).firstChild as HTMLElement; + + const text = stringToHTML(/* html */ ` +
+

+ Your grade history and charts stay on this device. + BetterSEQTA+ does not collect or store your analytics on our servers. +

+ +

What we store locally

+ + +

What we never do

+ + +

How refresh works

+

+ Refresh data loads released marks directly from SEQTA while you are logged in. + That traffic is between your browser and your school’s SEQTA site — not to us. +

+ +

Clearing your data

+

+ You can remove cached analytics any time by clearing this extension’s storage in + your browser settings. +

+ +

+ General plugin settings (such as cache duration in the Grade Analytics plugin + panel) may still sync if you use BetterSEQTA Cloud — but never your assessment + results or charts. +

+
+ `).firstChild as HTMLElement; + + openPopup({ + header, + content: [text], + animateSelector: ".whatsnewTextContainer *", + containerClass: "whatsnewContainer--scrollBody", + }); +} diff --git a/src/plugins/built-in/gradeAnalytics/styles.css b/src/plugins/built-in/gradeAnalytics/styles.css index c7dc3581..1c1f1343 100644 --- a/src/plugins/built-in/gradeAnalytics/styles.css +++ b/src/plugins/built-in/gradeAnalytics/styles.css @@ -103,6 +103,20 @@ gap: 1.25rem; } +.bsplus-analytics-header-actions { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 0.5rem; + flex-shrink: 0; +} + +@media (min-width: 480px) { + .bsplus-analytics-header-actions { + min-width: 10.5rem; + } +} + .bsplus-analytics-header-text h1 { margin: 0 0 0.35rem; font-size: 1.875rem; diff --git a/src/resources/updateimage1.webp b/src/resources/updateimage1.webp new file mode 100644 index 00000000..8ebc6f53 Binary files /dev/null and b/src/resources/updateimage1.webp differ diff --git a/src/seqta/content/MenuItemSVGKey.json b/src/seqta/content/MenuItemSVGKey.json index e8aaa8d8..41675c53 100644 --- a/src/seqta/content/MenuItemSVGKey.json +++ b/src/seqta/content/MenuItemSVGKey.json @@ -8,5 +8,6 @@ "portals": "\n \n", "reports": "\n \n", "settings": "\n \n", - "timetable": "\n \n" + "timetable": "\n \n", + "analytics": "\n \n \n" } diff --git a/src/seqta/utils/Openers/OpenPrivacyStatement.ts b/src/seqta/utils/Openers/OpenPrivacyStatement.ts index 4fc52682..579b2059 100644 --- a/src/seqta/utils/Openers/OpenPrivacyStatement.ts +++ b/src/seqta/utils/Openers/OpenPrivacyStatement.ts @@ -11,7 +11,7 @@ export function OpenPrivacyStatement() { ).firstChild as HTMLElement; const text = stringToHTML(/* html */ ` -
+

Privacy Policy

At BetterSEQTA+, we take your privacy seriously. We want to be completely transparent about how we handle your data.

@@ -43,6 +43,7 @@ export function OpenPrivacyStatement() { openPopup({ header, content: [text], + containerClass: "whatsnewContainer--scrollBody", }); } diff --git a/src/seqta/utils/Openers/OpenWhatsNewPopup.ts b/src/seqta/utils/Openers/OpenWhatsNewPopup.ts index 8b068f05..b9e4bb3d 100644 --- a/src/seqta/utils/Openers/OpenWhatsNewPopup.ts +++ b/src/seqta/utils/Openers/OpenWhatsNewPopup.ts @@ -19,27 +19,40 @@ export function OpenWhatsNewPopup(onDismissed?: () => void) { const video = document.createElement("video"); const source = document.createElement("source"); - source.setAttribute( - "src", - "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/update-video.webm", - ); - video.autoplay = true; - video.muted = true; - video.loop = true; - video.appendChild(source); - video.classList.add("whatsnewImg"); - imageContainer.appendChild(video); - attachPopupMediaFullscreen(video); + //source.setAttribute( + // "src", + // "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/update-video.webm", + //); + //video.autoplay = true; + //video.muted = true; + //video.loop = true; + //video.appendChild(source); + //video.classList.add("whatsnewImg"); + //imageContainer.appendChild(video); + //attachPopupMediaFullscreen(video); + + const heroImage = document.createElement("img"); + heroImage.src = + "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/updateimage1.webp"; + heroImage.alt = "BetterSEQTA+ update preview"; + heroImage.classList.add("whatsnewImg"); + imageContainer.appendChild(heroImage); + attachPopupMediaFullscreen(heroImage); const text = stringToHTML(/* html */ `
-

3.6.6 – Global Search improvements!

-
  • Tuned hybrid search and indexing reliability.
  • -
  • Clearer progress UI and green “Done!” when a pass finishes.
  • -
  • Merged duplicate course hits that opened the same page.
  • -
  • Reset Index reminds you to reload the tab to rebuild.
  • -
  • Index now captures all content accross the site, not just assessments and courses.
  • +

    3.7.0 – Grade Analytics, Global Search & SEQTA Engage Improvements

    +
  • Added Grade Analytics, new sidebar page with grade trend charts synced from SEQTA.
  • +
  • Added Grade distribution auto-detects your school’s letter scale from released marks for analytics page.
  • +
  • Added documents, notices, portals, folios, goals, and more to Global Search.
  • +
  • Added shortcuts to SEQTA Engage home page.
  • +
  • Added assessments overview and assessment weighting overrides for SEQTA Engage.
  • +
  • Added BetterSEQTA sidebar icons to SEQTA Engage.
  • +
  • Added runtime handlers for upcoming interactive theme.
  • +
  • Fixed BetterSEQTA sidebar injection issues on some pages.
  • +
  • Tweak Theme of the Month popup making it more clear about dismissals and respecting “Don’t show again”.
  • +
  • Fixed duplicate-result fixes.
  • 3.6.5 - Theme of the Month, custom message folders & assessment weighting overrides

  • Added Theme of the Month — a monthly featured theme popup with a link to view it in the theme store.
  • diff --git a/src/seqta/utils/Openers/PopupManager.ts b/src/seqta/utils/Openers/PopupManager.ts index ca44c68d..2f233235 100644 --- a/src/seqta/utils/Openers/PopupManager.ts +++ b/src/seqta/utils/Openers/PopupManager.ts @@ -53,6 +53,8 @@ interface OpenPopupOptions { afterClose?: () => void; /** When true, clears the post-update flag when this popup opens (What's New only). */ clearJustUpdated?: boolean; + /** Extra classes on `.whatsnewContainer` (e.g. `whatsnewContainer--scrollBody`). */ + containerClass?: string; } export function openPopup({ @@ -61,6 +63,7 @@ export function openPopup({ animateSelector = ".whatsnewTextContainer *", afterClose, clearJustUpdated = false, + containerClass, }: OpenPopupOptions = {}) { pendingAfterClose = afterClose; @@ -70,6 +73,11 @@ export function openPopup({ const container = document.createElement("div"); container.classList.add("whatsnewContainer"); + if (containerClass) { + for (const name of containerClass.split(/\s+/)) { + if (name) container.classList.add(name); + } + } if (header) container.append(header); for (const node of content) if (node) container.append(node); diff --git a/src/seqta/utils/cloudSettingsSync.ts b/src/seqta/utils/cloudSettingsSync.ts index d474fc76..a36c4e85 100644 --- a/src/seqta/utils/cloudSettingsSync.ts +++ b/src/seqta/utils/cloudSettingsSync.ts @@ -40,8 +40,11 @@ export const SENSITIVE_DEVICE_STORAGE_KEYS_EXACT = [ "plugin.assessments-average.storage.weightings", ] as const; -/** e.g. any future `plugin.global-search.storage.*` keys in chrome.storage */ -export const SENSITIVE_DEVICE_STORAGE_KEY_PREFIXES = ["plugin.global-search.storage."] as const; +/** School-specific caches; never sync across devices. */ +export const SENSITIVE_DEVICE_STORAGE_KEY_PREFIXES = [ + "plugin.global-search.storage.", + "bsplus.analytics.", +] as const; const CLIENT_ONLY_CLOUD_KEYS_EXACT = [ BSPLUS_CLOUD_KNOWN_REMOTE_UPDATED_AT_KEY,