mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-05 19:24:39 +00:00
feat: prep fopr v3.7.0 as well as minor tweaks
This commit is contained in:
@@ -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.
|
||||
|
||||
+1
-1
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 @@
|
||||
<p class="bsplus-analytics-meta">Last updated: {formattedTimestamp()}</p>
|
||||
{/if}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="bsplus-analytics-btn bsplus-analytics-btn-primary"
|
||||
disabled={syncing}
|
||||
onclick={() => runSync()}
|
||||
>
|
||||
{syncing ? "Syncing…" : "Refresh data"}
|
||||
</button>
|
||||
<div class="bsplus-analytics-header-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="bsplus-analytics-btn bsplus-analytics-btn-primary"
|
||||
disabled={syncing}
|
||||
onclick={() => runSync()}
|
||||
>
|
||||
{syncing ? "Syncing…" : "Refresh data"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="bsplus-analytics-btn bsplus-analytics-btn-ghost"
|
||||
onclick={() => openAnalyticsPrivacyPopup()}
|
||||
>
|
||||
Privacy notice
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{#if error}
|
||||
|
||||
@@ -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 = `<label><svg style="width:24px;height:24px" viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 8h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></svg><span>Analytics</span></label>`;
|
||||
analyticsItem.innerHTML = `<label>${ANALYTICS_MENU_ICON}<span>Analytics</span></label>`;
|
||||
|
||||
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 });
|
||||
|
||||
@@ -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 */
|
||||
`<div class="whatsnewHeader">
|
||||
<h1>Privacy notice</h1>
|
||||
<p>Grade Analytics on this device</p>
|
||||
</div>`,
|
||||
).firstChild as HTMLElement;
|
||||
|
||||
const text = stringToHTML(/* html */ `
|
||||
<div class="whatsnewTextContainer privacyStatement">
|
||||
<p style="margin-top: 0;">
|
||||
<strong>Your grade history and charts stay on this device.</strong>
|
||||
BetterSEQTA+ does not collect or store your analytics on our servers.
|
||||
</p>
|
||||
|
||||
<h3>What we store locally</h3>
|
||||
<ul style="text-align: left; margin: 10px 0;">
|
||||
<li>Assessment results and subjects used for trends, distribution, and the table</li>
|
||||
<li>Chart preferences (for example, grade distribution grouping) for your school account</li>
|
||||
<li>A cache timestamp so Refresh data knows when to fetch from SEQTA again</li>
|
||||
</ul>
|
||||
|
||||
<h3>What we never do</h3>
|
||||
<ul style="text-align: left; margin: 10px 0;">
|
||||
<li>Upload analytics data to BetterSEQTA Cloud or any BetterSEQTA server</li>
|
||||
<li>Include analytics in automatic cloud settings backup or restore</li>
|
||||
<li>Send your grades to third-party analytics or tracking services</li>
|
||||
</ul>
|
||||
|
||||
<h3>How refresh works</h3>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<h3>Clearing your data</h3>
|
||||
<p>
|
||||
You can remove cached analytics any time by clearing this extension’s storage in
|
||||
your browser settings.
|
||||
</p>
|
||||
|
||||
<p style="font-weight: 600;">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
`).firstChild as HTMLElement;
|
||||
|
||||
openPopup({
|
||||
header,
|
||||
content: [text],
|
||||
animateSelector: ".whatsnewTextContainer *",
|
||||
containerClass: "whatsnewContainer--scrollBody",
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
@@ -8,5 +8,6 @@
|
||||
"portals": "<svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\">\n <path fill=\"currentColor\" d=\"M15,4A8,8 0 0,1 23,12A8,8 0 0,1 15,20A8,8 0 0,1 7,12A8,8 0 0,1 15,4M15,18A6,6 0 0,0 21,12A6,6 0 0,0 15,6A6,6 0 0,0 9,12A6,6 0 0,0 15,18M3,12C3,14.61 4.67,16.83 7,17.65V19.74C3.55,18.85 1,15.73 1,12C1,8.27 3.55,5.15 7,4.26V6.35C4.67,7.17 3,9.39 3,12Z\" />\n</svg>",
|
||||
"reports": "<svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\">\n <path fill=\"currentColor\" d=\"M18 2H12V9L9.5 7.5L7 9V2H6A2 2 0 0 0 4 4V20A2 2 0 0 0 6 22H18A2 2 0 0 0 20 20V4A2 2 0 0 0 18 2M14 12A2 2 0 1 1 12 14A2 2 0 0 1 14 12M18 20H10V19C10 17.67 12.67 17 14 17S18 17.67 18 19Z\" />\n</svg>",
|
||||
"settings": "<svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\">\n <path fill=\"currentColor\" d=\"M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.03 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.67 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.03 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z\" />\n</svg>",
|
||||
"timetable": "<svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\">\n <path fill=\"currentColor\" d=\"M9,10V12H7V10H9M13,10V12H11V10H13M17,10V12H15V10H17M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5A2,2 0 0,1 5,3H6V1H8V3H16V1H18V3H19M19,19V8H5V19H19M9,14V16H7V14H9M13,14V16H11V14H13M17,14V16H15V14H17Z\" />\n</svg>"
|
||||
"timetable": "<svg style=\"width:24px;height:24px\" viewBox=\"0 0 24 24\">\n <path fill=\"currentColor\" d=\"M9,10V12H7V10H9M13,10V12H11V10H13M17,10V12H15V10H17M19,3A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5C3.89,21 3,20.1 3,19V5A2,2 0 0,1 5,3H6V1H8V3H16V1H18V3H19M19,19V8H5V19H19M9,14V16H7V14H9M13,14V16H11V14H13M17,14V16H15V14H17Z\" />\n</svg>",
|
||||
"analytics": "<svg style=\"width:24px;height:24px\" viewBox=\"0 0 32 32\" aria-hidden=\"true\">\n <path fill=\"currentColor\" d=\"M4,2H2V28a2,2,0,0,0,2,2H30V28H4Z\" />\n <path fill=\"currentColor\" d=\"M30,9H23v2h3.59L19,18.59l-4.29-4.3a1,1,0,0,0-1.42,0L6,21.59,7.41,23,14,16.41l4.29,4.3a1,1,0,0,0,1.42,0L28,12.41V16h2Z\" />\n</svg>"
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ export function OpenPrivacyStatement() {
|
||||
).firstChild as HTMLElement;
|
||||
|
||||
const text = stringToHTML(/* html */ `
|
||||
<div class="whatsnewTextContainer" style="overflow-y: auto; max-height: 60vh;">
|
||||
<div class="whatsnewTextContainer privacyStatement">
|
||||
<h2 style="margin-top: 0;">Privacy Policy</h2>
|
||||
<p>At BetterSEQTA+, we take your privacy seriously. We want to be completely transparent about how we handle your data.</p>
|
||||
|
||||
@@ -43,6 +43,7 @@ export function OpenPrivacyStatement() {
|
||||
openPopup({
|
||||
header,
|
||||
content: [text],
|
||||
containerClass: "whatsnewContainer--scrollBody",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */ `
|
||||
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: auto;">
|
||||
|
||||
<h1>3.6.6 – Global Search improvements!</h1>
|
||||
<li>Tuned hybrid search and indexing reliability.</li>
|
||||
<li>Clearer progress UI and green “Done!” when a pass finishes.</li>
|
||||
<li>Merged duplicate course hits that opened the same page.</li>
|
||||
<li>Reset Index reminds you to reload the tab to rebuild.</li>
|
||||
<li>Index now captures all content accross the site, not just assessments and courses.</li>
|
||||
<h1>3.7.0 – Grade Analytics, Global Search & SEQTA Engage Improvements</h1>
|
||||
<li>Added Grade Analytics, new sidebar page with grade trend charts synced from SEQTA.</li>
|
||||
<li>Added Grade distribution auto-detects your school’s letter scale from released marks for analytics page.</li>
|
||||
<li>Added documents, notices, portals, folios, goals, and more to Global Search.</li>
|
||||
<li>Added shortcuts to SEQTA Engage home page.</li>
|
||||
<li>Added assessments overview and assessment weighting overrides for SEQTA Engage.</li>
|
||||
<li>Added BetterSEQTA sidebar icons to SEQTA Engage.</li>
|
||||
<li>Added runtime handlers for upcoming interactive theme.</li>
|
||||
<li>Fixed BetterSEQTA sidebar injection issues on some pages.</li>
|
||||
<li>Tweak Theme of the Month popup making it more clear about dismissals and respecting “Don’t show again”.</li>
|
||||
<li>Fixed duplicate-result fixes.</li>
|
||||
|
||||
<h1>3.6.5 - Theme of the Month, custom message folders & assessment weighting overrides</h1>
|
||||
<li>Added Theme of the Month — a monthly featured theme popup with a link to view it in the theme store.</li>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user