mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-06-06 11:44:40 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a151e7a07e | |||
| 5b590512ee | |||
| 3ff8ef144a | |||
| d9abed1c5d | |||
| 82a789bbec | |||
| ce6538f850 | |||
| 979ae7149f | |||
| 6e71437fe8 | |||
| 940ecf8714 | |||
| e0cc2e0fdf | |||
| 5a19ef92e8 | |||
| 0a3781e9c2 | |||
| a2e39c9d84 | |||
| 520abbb5c3 | |||
| d0a11da15f | |||
| fd5802f9a3 | |||
| 380d829d19 | |||
| 702528fb0c | |||
| 2c077bc755 | |||
| fd86e57442 | |||
| 60ce18280e | |||
| 668dbfd78b | |||
| 810aa17f15 | |||
| b64558e50a | |||
| 9b969bd708 | |||
| 1945f7c592 | |||
| 3e26d9af3c | |||
| 3c8d7e246b | |||
| 2e56518330 | |||
| e67f3110e0 | |||
| a67f4d2e25 | |||
| d6025140fd | |||
| 88e9ddf29c | |||
| 11adc4f933 | |||
| 15691e8d94 | |||
| 754b8d0589 | |||
| 1d634d0da1 | |||
| 7136de90be | |||
| 466628479e | |||
| 9c08d0bac2 | |||
| 6c5320007f | |||
| 4734a443b4 | |||
| 7c38e1dc29 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "betterseqtaplus",
|
"name": "betterseqtaplus",
|
||||||
"version": "3.4.11",
|
"version": "3.4.13",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Enhance SEQTA Learn's usability and aesthetics! A fork of BetterSEQTA to continue development add add heaps more features!",
|
"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",
|
"browserslist": "> 0.5%, last 2 versions, not dead",
|
||||||
|
|||||||
+1
-1
@@ -108,7 +108,7 @@ function getDefaultValues(): SettingsState {
|
|||||||
originalSelectedColor: "",
|
originalSelectedColor: "",
|
||||||
DarkMode: true,
|
DarkMode: true,
|
||||||
animations: !isLowEndDevice,
|
animations: !isLowEndDevice,
|
||||||
assessmentsAverage: true,
|
assessmentsAverage: false,
|
||||||
defaultPage: "home",
|
defaultPage: "home",
|
||||||
shortcuts: [
|
shortcuts: [
|
||||||
{
|
{
|
||||||
|
|||||||
+64
-22
@@ -57,7 +57,8 @@ select {
|
|||||||
transition: 200ms;
|
transition: 200ms;
|
||||||
background: var(--auto-background) !important;
|
background: var(--auto-background) !important;
|
||||||
}
|
}
|
||||||
* {
|
:root * {
|
||||||
|
font-family: Rubik, sans-serif !important;
|
||||||
--theme-fg-parts: white;
|
--theme-fg-parts: white;
|
||||||
}
|
}
|
||||||
.extension-editor {
|
.extension-editor {
|
||||||
@@ -158,6 +159,16 @@ select {
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
#main {
|
||||||
|
> .timetablepage {
|
||||||
|
> .quickbar {
|
||||||
|
.gutter {
|
||||||
|
border-bottom-left-radius: 15px;
|
||||||
|
border-bottom-right-radius: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.forums {
|
.forums {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
@@ -813,8 +824,8 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
[aria-labelledby="lixycoxs-tab-1"] [minlength="0"] {
|
[aria-labelledby="lixycoxs-tab-1"] [minlength="0"] {
|
||||||
min-height: 128px !important;
|
min-height: 128px !important;
|
||||||
}
|
}
|
||||||
.student #menu > ul::before {
|
body.student #menu > ul::before {
|
||||||
background-image: var(--betterseqta-logo);
|
background-image: var(--betterseqta-logo) !important;
|
||||||
position: -webkit-sticky;
|
position: -webkit-sticky;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
@@ -823,11 +834,18 @@ div > ol:has(.uiFileHandlerWrapper) {
|
|||||||
box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, 0.2);
|
box-shadow: 0px 0px 4px 2px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
html.transparencyEffects [class*="ResourceList__ResourceItem___voTSd"],
|
html.transparencyEffects [class*="BasicPanel__BasicPanel___q92_U"] > ol > li {
|
||||||
html.transparencyEffects [class*="ResourceList__ResourceItem___voTSd"] [class*="ResourceList__name___ydvDT"] {
|
|
||||||
border-bottom: none !important;
|
border-bottom: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html.transparencyEffects
|
||||||
|
[class*="BasicPanel__BasicPanel___q92_U"]
|
||||||
|
> ol
|
||||||
|
> li
|
||||||
|
+ li {
|
||||||
|
border-top: 1px solid var(--theme-offset-bg);
|
||||||
|
}
|
||||||
|
|
||||||
.assessmentsWrapper .message {
|
.assessmentsWrapper .message {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -1026,8 +1044,8 @@ html.transparencyEffects [class*="ResourceList__ResourceItem___voTSd"] [class*="
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#title {
|
#title {
|
||||||
background: var(--background-primary);
|
background: var(--background-primary) !important;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary) !important;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding-right: 56px !important;
|
padding-right: 56px !important;
|
||||||
@@ -1761,7 +1779,9 @@ iframe.userHTML {
|
|||||||
background: var(--auto-background);
|
background: var(--auto-background);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
scale: 0.95;
|
scale: 0.95;
|
||||||
transition: opacity 0.2s ease-out, scale 0.1s ease-out;
|
transition:
|
||||||
|
opacity 0.2s ease-out,
|
||||||
|
scale 0.1s ease-out;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
@@ -1775,7 +1795,6 @@ iframe.userHTML {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
scale: 1;
|
scale: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1799,7 +1818,9 @@ iframe.userHTML {
|
|||||||
.dark .programmeNavigator .navigator {
|
.dark .programmeNavigator .navigator {
|
||||||
.search {
|
.search {
|
||||||
background: var(--background-secondary) !important;
|
background: var(--background-secondary) !important;
|
||||||
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.1), inset 0px 0px 15px 0px rgba(0, 0, 0, 0.1) !important;
|
box-shadow:
|
||||||
|
0px 0px 10px 0px rgba(0, 0, 0, 0.1),
|
||||||
|
inset 0px 0px 15px 0px rgba(0, 0, 0, 0.1) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.dark #main > .course > .content > h1 {
|
.dark #main > .course > .content > h1 {
|
||||||
@@ -2186,7 +2207,6 @@ div.bar.flat {
|
|||||||
border-radius: 12px !important;
|
border-radius: 12px !important;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
color: #fff !important;
|
|
||||||
padding: 16px !important;
|
padding: 16px !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
@@ -2194,10 +2214,10 @@ div.bar.flat {
|
|||||||
display: flex !important;
|
display: flex !important;
|
||||||
align-items: flex-start !important;
|
align-items: flex-start !important;
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
|
color: var(--text-primary) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.cke_toolbox > .cke_toolbar > .cke_toolgroup > .cke_button {
|
.cke_toolbox > .cke_toolbar > .cke_toolgroup > .cke_button {
|
||||||
background: var(--background-secondary) !important;
|
background: var(--background-secondary) !important;
|
||||||
color: var(--text-primary) !important;
|
color: var(--text-primary) !important;
|
||||||
@@ -3339,6 +3359,22 @@ div.day-empty {
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
transform-origin: center center;
|
transform-origin: center center;
|
||||||
}
|
}
|
||||||
|
.whatsnewTextContainer.privacyStatement p {
|
||||||
|
margin-bottom: 1.5ex;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.whatsnewTextContainer.privacyStatement a {
|
||||||
|
background: rgba(184, 184, 184, 0.1);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
|
.dark .whatsnewTextContainer.privacyStatement a {
|
||||||
|
background: rgba(7, 7, 7, 0.1);
|
||||||
|
}
|
||||||
.whatsnewHeader {
|
.whatsnewHeader {
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -3373,6 +3409,7 @@ div.day-empty {
|
|||||||
.whatsnewImg {
|
.whatsnewImg {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
width: 90%;
|
width: 90%;
|
||||||
|
aspect-ratio: 16 / 10;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
|
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
@@ -3399,7 +3436,6 @@ div.day-empty {
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #9a3412;
|
color: #9a3412;
|
||||||
background-color: #ffedd569;
|
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
border: 1px solid rgba(253, 186, 140, 0.3);
|
border: 1px solid rgba(253, 186, 140, 0.3);
|
||||||
background-color: #ffedd5;
|
background-color: #ffedd5;
|
||||||
@@ -3750,7 +3786,12 @@ div.day-empty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.notice-unified-content {
|
.notice-unified-content {
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
font-weight: inherit !important;
|
font-weight: inherit !important;
|
||||||
@@ -3771,7 +3812,7 @@ div.day-empty {
|
|||||||
font-size: 20px !important; // Nice middle ground - not too big, not too small
|
font-size: 20px !important; // Nice middle ground - not too big, not too small
|
||||||
font-weight: 600 !important;
|
font-weight: 600 !important;
|
||||||
color: var(--text-primary) !important;
|
color: var(--text-primary) !important;
|
||||||
margin: 0 0 12px 0 !important;
|
margin: 0 0 12px !important;
|
||||||
line-height: 1.3 !important;
|
line-height: 1.3 !important;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@@ -3842,7 +3883,8 @@ div.day-empty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul, ol {
|
ul,
|
||||||
|
ol {
|
||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
@@ -3882,7 +3924,6 @@ button.notice-close-btn {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 18px;
|
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
transition: all 0.2s ease !important;
|
transition: all 0.2s ease !important;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -3940,13 +3981,13 @@ button.notice-close-btn {
|
|||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
margin: 16px 20px 20px 20px;
|
margin: 16px 20px 20px;
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-modal-body {
|
.notice-modal-body {
|
||||||
padding: 0 20px 20px 20px;
|
padding: 0 20px 20px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
@@ -3989,7 +4030,8 @@ button.notice-close-btn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul, ol {
|
ul,
|
||||||
|
ol {
|
||||||
margin: 12px 0;
|
margin: 12px 0;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
}
|
}
|
||||||
@@ -4026,11 +4068,11 @@ button.notice-close-btn {
|
|||||||
|
|
||||||
.notice-modal-title {
|
.notice-modal-title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
margin: 12px 16px 16px 16px;
|
margin: 12px 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-modal-body {
|
.notice-modal-body {
|
||||||
padding: 0 16px 16px 16px;
|
padding: 0 16px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notice-card {
|
.notice-card {
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"last_updated": "2024-06-15T12:00:00Z",
|
||||||
|
"whatsnew_html": "<div class=\"whatsnewTextContainer\" style=\"overflow-y: auto; font-size: 1.3rem; line-height: 1.6;\"><p>It has come to our attention that several schools have expressed concerns about BetterSEQTA+. This is very disheartening, so we have decided to release a statement on the situation.</p><p>To view our privacy policy, please click the <strong>shield icon</strong> in the settings menu, or <a href=\"https://betterseqta.org/privacy\" target=\"_blank\" rel=\"noopener noreferrer\" id=\"privacy-link\" style=\"color: inherit; text-decoration: underline; cursor: pointer; white-space: nowrap;\">click here</a>.</p><p style=\"font-weight: bold; margin-top: 15px;\">We never collect any information from you, and aim to provide the best features possible.</p></div>"
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
import { animate } from 'motion';
|
||||||
|
|
||||||
|
let { onConfirm, onCancel, title, message } = $props<{
|
||||||
|
onConfirm: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let modalElement: HTMLElement;
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (modalElement) {
|
||||||
|
animate(
|
||||||
|
modalElement,
|
||||||
|
{ scale: [0.9, 1], opacity: [0, 1] },
|
||||||
|
{
|
||||||
|
type: 'spring',
|
||||||
|
stiffness: 300,
|
||||||
|
damping: 25
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex fixed inset-0 z-[10000] justify-center items-center bg-black/50"
|
||||||
|
style="position: fixed; top: 0; left: 0; right: 0; bottom: 0;"
|
||||||
|
onclick={(e) => {
|
||||||
|
if (e.target === e.currentTarget) onCancel();
|
||||||
|
}}
|
||||||
|
onkeydown={(e) => {
|
||||||
|
if (e.key === 'Escape') onCancel();
|
||||||
|
}}
|
||||||
|
role="button"
|
||||||
|
tabindex="-1"
|
||||||
|
transition:fade={{ duration: 150 }}
|
||||||
|
>
|
||||||
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
|
<div
|
||||||
|
bind:this={modalElement}
|
||||||
|
class="p-4 mx-4 w-full max-w-md bg-white rounded-2xl shadow-2xl dark:bg-zinc-800"
|
||||||
|
onclick={(e) => e.stopPropagation()}
|
||||||
|
onkeydown={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
<h2 class="mb-3 text-xl font-bold text-gray-900 dark:text-white">
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="mb-6 text-lg text-gray-700 whitespace-pre-line dark:text-gray-300">
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex gap-3 justify-end">
|
||||||
|
<button
|
||||||
|
onclick={onCancel}
|
||||||
|
class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-lg transition-colors hover:bg-gray-200 dark:bg-zinc-700 dark:text-gray-200 dark:hover:bg-zinc-600"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onclick={onConfirm}
|
||||||
|
class="px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-lg shadow-inner transition-colors hover:bg-green-700 dark:bg-green-500 dark:hover:bg-green-600"
|
||||||
|
>
|
||||||
|
Enable
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
@@ -11,13 +11,16 @@
|
|||||||
|
|
||||||
import { closeExtensionPopup } from "@/seqta/utils/Closers/closeExtensionPopup";
|
import { closeExtensionPopup } from "@/seqta/utils/Closers/closeExtensionPopup";
|
||||||
import { OpenAboutPage } from "@/seqta/utils/Openers/OpenAboutPage";
|
import { OpenAboutPage } from "@/seqta/utils/Openers/OpenAboutPage";
|
||||||
import { OpenWhatsNewPopup } from "@/seqta/utils/Whatsnew";
|
import { OpenWhatsNewPopup } from "@/seqta/utils/Openers/OpenWhatsNewPopup";
|
||||||
import { OpenMinecraftServerPopup } from "@/seqta/utils/AboutMinecraftServer";
|
//import { OpenMinecraftServerPopup } from "@/seqta/utils/Openers/OpenMinecraftServerPopup";
|
||||||
|
|
||||||
import ColourPicker from "../components/ColourPicker.svelte";
|
import ColourPicker from "../components/ColourPicker.svelte";
|
||||||
|
import DisclaimerModal from "../components/DisclaimerModal.svelte";
|
||||||
import { settingsPopup } from "../hooks/SettingsPopup";
|
import { settingsPopup } from "../hooks/SettingsPopup";
|
||||||
|
|
||||||
let devModeSequence = "";
|
let devModeSequence = "";
|
||||||
|
let showDisclaimerModal = $state(false);
|
||||||
|
let disclaimerCallbacks = $state<{ onConfirm: () => void, onCancel: () => void } | null>(null);
|
||||||
|
|
||||||
const handleDevModeToggle = () => {
|
const handleDevModeToggle = () => {
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
@@ -50,14 +53,24 @@
|
|||||||
closeExtensionPopup();
|
closeExtensionPopup();
|
||||||
};
|
};
|
||||||
|
|
||||||
const openMinecraftServer = () => {
|
/* const openMinecraftServer = () => {
|
||||||
OpenMinecraftServerPopup();
|
OpenMinecraftServerPopup();
|
||||||
closeExtensionPopup();
|
closeExtensionPopup();
|
||||||
|
}; */
|
||||||
|
|
||||||
|
const openPrivacyStatement = () => {
|
||||||
|
window.open("https://betterseqta.org/privacy", "_blank");
|
||||||
|
closeExtensionPopup();
|
||||||
};
|
};
|
||||||
|
|
||||||
let { standalone } = $props<{ standalone?: boolean }>();
|
let { standalone } = $props<{ standalone?: boolean }>();
|
||||||
let showColourPicker = $state<boolean>(false);
|
let showColourPicker = $state<boolean>(false);
|
||||||
|
|
||||||
|
const showDisclaimer = (onConfirm: () => void, onCancel: () => void) => {
|
||||||
|
disclaimerCallbacks = { onConfirm, onCancel };
|
||||||
|
showDisclaimerModal = true;
|
||||||
|
};
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
settingsPopup.addListener(() => {
|
settingsPopup.addListener(() => {
|
||||||
showColourPicker = false;
|
showColourPicker = false;
|
||||||
@@ -101,23 +114,32 @@
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{#if !standalone}
|
{#if !standalone}
|
||||||
|
<div class="flex absolute top-1 right-1 gap-1 items-center">
|
||||||
<button
|
<button
|
||||||
onclick={openAbout}
|
onclick={openAbout}
|
||||||
class="absolute top-1 right-[62px] w-8 h-8 text-lg rounded-xl font-IconFamily bg-zinc-100 dark:bg-zinc-700"
|
class="flex justify-center items-center w-8 h-8 text-lg rounded-xl font-IconFamily bg-zinc-100 dark:bg-zinc-700"
|
||||||
>
|
>
|
||||||
{"\ueb73"}
|
{"\ueb73"}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onclick={openChangelog}
|
onclick={openChangelog}
|
||||||
class="absolute top-1 right-10 w-8 h-8 text-lg rounded-xl font-IconFamily bg-zinc-100 dark:bg-zinc-700"
|
class="flex justify-center items-center w-8 h-8 text-lg rounded-xl font-IconFamily bg-zinc-100 dark:bg-zinc-700"
|
||||||
>
|
>
|
||||||
{"\ue929"}
|
{"\ue929"}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
onclick={openPrivacyStatement}
|
||||||
|
class="flex justify-center items-center w-8 h-8 text-lg rounded-xl font-IconFamily bg-zinc-100 dark:bg-zinc-700"
|
||||||
|
aria-label="Privacy Statement"
|
||||||
|
>
|
||||||
|
{"\uecba"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- <button
|
||||||
onclick={openMinecraftServer}
|
onclick={openMinecraftServer}
|
||||||
class="absolute top-1 right-1 w-8 h-8 bg-zinc-100 dark:bg-zinc-700 rounded-xl p-1"
|
class="flex justify-center items-center p-1 w-8 h-8 rounded-xl bg-zinc-100 dark:bg-zinc-700"
|
||||||
aria-label="Open Minecraft Server"
|
aria-label="Open Minecraft Server"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
@@ -247,7 +269,8 @@
|
|||||||
transform="translate(18,10)"
|
transform="translate(18,10)"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button> -->
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -256,7 +279,7 @@
|
|||||||
{
|
{
|
||||||
title: "Settings",
|
title: "Settings",
|
||||||
Content: Settings,
|
Content: Settings,
|
||||||
props: { showColourPicker: openColourPicker },
|
props: { showColourPicker: openColourPicker, showDisclaimer },
|
||||||
},
|
},
|
||||||
{ title: "Shortcuts", Content: Shortcuts },
|
{ title: "Shortcuts", Content: Shortcuts },
|
||||||
{ title: "Themes", Content: Theme },
|
{ title: "Themes", Content: Theme },
|
||||||
@@ -272,3 +295,27 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if showDisclaimerModal && disclaimerCallbacks}
|
||||||
|
<DisclaimerModal
|
||||||
|
title="Assessment Averages Disclaimer"
|
||||||
|
message="This feature calculates a simple average of your assessment grades. It does not take into account:
|
||||||
|
• Assessment weightings
|
||||||
|
• Different grading scales
|
||||||
|
• Other factors used in official reports
|
||||||
|
|
||||||
|
The displayed average may be inaccurate compared to your actual marks found in reports.
|
||||||
|
|
||||||
|
Do you want to enable this feature?"
|
||||||
|
onConfirm={() => {
|
||||||
|
disclaimerCallbacks?.onConfirm();
|
||||||
|
showDisclaimerModal = false;
|
||||||
|
disclaimerCallbacks = null;
|
||||||
|
}}
|
||||||
|
onCancel={() => {
|
||||||
|
disclaimerCallbacks?.onCancel();
|
||||||
|
showDisclaimerModal = false;
|
||||||
|
disclaimerCallbacks = null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
import type { SettingsList } from "@/interface/types/SettingsProps"
|
import type { SettingsList } from "@/interface/types/SettingsProps"
|
||||||
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
|
import { settingsState } from "@/seqta/utils/listeners/SettingsState.ts"
|
||||||
import PickerSwatch from "@/interface/components/PickerSwatch.svelte"
|
import PickerSwatch from "@/interface/components/PickerSwatch.svelte"
|
||||||
|
import { showPrivacyNotification } from "@/seqta/utils/Openers/OpenPrivacyNotification"
|
||||||
|
import { closeExtensionPopup } from "@/seqta/utils/Closers/closeExtensionPopup"
|
||||||
|
|
||||||
import { getAllPluginSettings } from "@/plugins"
|
import { getAllPluginSettings } from "@/plugins"
|
||||||
import type { BooleanSetting, StringSetting, NumberSetting, SelectSetting, ButtonSetting, HotkeySetting, ComponentSetting } from "@/plugins/core/types"
|
import type { BooleanSetting, StringSetting, NumberSetting, SelectSetting, ButtonSetting, HotkeySetting, ComponentSetting } from "@/plugins/core/types"
|
||||||
@@ -90,7 +92,10 @@
|
|||||||
loadPluginSettings();
|
loadPluginSettings();
|
||||||
})
|
})
|
||||||
|
|
||||||
const { showColourPicker } = $props<{ showColourPicker: () => void }>();
|
const { showColourPicker, showDisclaimer } = $props<{
|
||||||
|
showColourPicker: () => void;
|
||||||
|
showDisclaimer: (onConfirm: () => void, onCancel: () => void) => void;
|
||||||
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet Setting({ title, description, Component, props }: SettingsList) }
|
{#snippet Setting({ title, description, Component, props }: SettingsList) }
|
||||||
@@ -222,7 +227,20 @@
|
|||||||
<div>
|
<div>
|
||||||
<Switch
|
<Switch
|
||||||
state={pluginSettingsValues[plugin.pluginId]?.enabled ?? true}
|
state={pluginSettingsValues[plugin.pluginId]?.enabled ?? true}
|
||||||
onChange={(value) => updatePluginSetting(plugin.pluginId, 'enabled', value)}
|
onChange={async (value) => {
|
||||||
|
if (plugin.pluginId === 'assessments-average' && value === true) {
|
||||||
|
showDisclaimer(
|
||||||
|
async () => {
|
||||||
|
await updatePluginSetting(plugin.pluginId, 'enabled', true);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// Do nothing on cancel
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await updatePluginSetting(plugin.pluginId, 'enabled', value);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -340,6 +358,25 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex justify-between items-center px-4 py-3">
|
||||||
|
<div class="pr-4">
|
||||||
|
<h2 class="text-sm font-bold">Show Privacy Notification</h2>
|
||||||
|
<p class="text-xs">Show the privacy notification popup on next page load</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
onClick={async () => {
|
||||||
|
settingsState.privacyStatementShown = false;
|
||||||
|
settingsState.privacyStatementLastUpdated = undefined;
|
||||||
|
closeExtensionPopup();
|
||||||
|
// Small delay to ensure popup is closed before showing notification
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
await showPrivacyNotification();
|
||||||
|
}}
|
||||||
|
text="Show Now"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const updatedFirefoxManifest = {
|
|||||||
},
|
},
|
||||||
browser_specific_settings: {
|
browser_specific_settings: {
|
||||||
gecko: {
|
gecko: {
|
||||||
id: pkg.author.email,
|
id: "betterseqta@betterseqta.com",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const notificationCollectorPlugin: Plugin<{}, NotificationCollectorStorage> = {
|
|||||||
"[class*='notifications__bubble___']",
|
"[class*='notifications__bubble___']",
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
|
|
||||||
if (api.storage.lastNotificationCount !== 0) {
|
if (alertDiv && api.storage.lastNotificationCount !== 0) {
|
||||||
alertDiv.textContent = api.storage.lastNotificationCount.toString();
|
alertDiv.textContent = api.storage.lastNotificationCount.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,13 +74,17 @@ const notificationCollectorPlugin: Plugin<{}, NotificationCollectorStorage> = {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[BetterSEQTA+] Error fetching notifications:", error);
|
console.error("[BetterSEQTA+] Error fetching notifications:", error);
|
||||||
api.storage.consecutiveErrors = (api.storage.consecutiveErrors || 0) + 1;
|
api.storage.consecutiveErrors =
|
||||||
|
(api.storage.consecutiveErrors || 0) + 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNextInterval = () => {
|
const getNextInterval = () => {
|
||||||
// Exponential backoff on errors, max 5 minutes
|
// Exponential backoff on errors, max 5 minutes
|
||||||
const errorMultiplier = Math.min(Math.pow(2, api.storage.consecutiveErrors || 0), 10);
|
const errorMultiplier = Math.min(
|
||||||
|
Math.pow(2, api.storage.consecutiveErrors || 0),
|
||||||
|
10,
|
||||||
|
);
|
||||||
return Math.min(baseInterval * errorMultiplier, maxInterval);
|
return Math.min(baseInterval * errorMultiplier, maxInterval);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,7 +96,8 @@ const notificationCollectorPlugin: Plugin<{}, NotificationCollectorStorage> = {
|
|||||||
const interval = getNextInterval();
|
const interval = getNextInterval();
|
||||||
pollInterval = window.setTimeout(() => {
|
pollInterval = window.setTimeout(() => {
|
||||||
checkNotifications().then(() => {
|
checkNotifications().then(() => {
|
||||||
if (pollInterval) { // Only continue if not stopped
|
if (pollInterval) {
|
||||||
|
// Only continue if not stopped
|
||||||
scheduleNext();
|
scheduleNext();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -124,14 +129,16 @@ const notificationCollectorPlugin: Plugin<{}, NotificationCollectorStorage> = {
|
|||||||
isVisible = !document.hidden;
|
isVisible = !document.hidden;
|
||||||
if (isVisible && !pollInterval) {
|
if (isVisible && !pollInterval) {
|
||||||
// Resume polling when tab becomes visible
|
// Resume polling when tab becomes visible
|
||||||
const alertDiv = document.querySelector("[class*='notifications__bubble___']");
|
const alertDiv = document.querySelector(
|
||||||
|
"[class*='notifications__bubble___']",
|
||||||
|
);
|
||||||
if (alertDiv) {
|
if (alertDiv) {
|
||||||
startPolling();
|
startPolling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
document.addEventListener("visibilitychange", handleVisibilityChange);
|
||||||
|
|
||||||
api.seqta.onMount("[class*='notifications__bubble___']", (_) => {
|
api.seqta.onMount("[class*='notifications__bubble___']", (_) => {
|
||||||
startPolling();
|
startPolling();
|
||||||
@@ -139,7 +146,7 @@ const notificationCollectorPlugin: Plugin<{}, NotificationCollectorStorage> = {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
stopPolling();
|
stopPolling();
|
||||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,12 +8,16 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
box-shadow: 0 0 0 3px #000000;
|
box-shadow: 0 0 0 3px #000000;
|
||||||
|
transition: box-shadow 0.05s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .userInfoImg {
|
.dark .userInfoImg {
|
||||||
box-shadow: 0 0 0 3px #ffffff;
|
box-shadow: 0 0 0 3px #ffffff;
|
||||||
|
transition: box-shadow 0.05s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.userInfosvgdiv {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
filter: invert(0) !important;
|
.userInfoImg {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -147,14 +147,21 @@ export class ThemeManager {
|
|||||||
public async initialize(): Promise<void> {
|
public async initialize(): Promise<void> {
|
||||||
console.debug("[ThemeManager] Starting initialization");
|
console.debug("[ThemeManager] Starting initialization");
|
||||||
try {
|
try {
|
||||||
// Check if theme creator was open during reload
|
const neumorphicThemeId = "9a9786d1-b5fc-4a91-8c7a-f8bf7f7679ad";
|
||||||
|
const migrationCSS = "#title {\nbackground: transparent !important;\n}";
|
||||||
|
|
||||||
|
const theme = (await localforage.getItem(neumorphicThemeId)) as CustomTheme | null;
|
||||||
|
if (theme && theme.CustomCSS && !theme.CustomCSS.includes("#title {\nbackground: transparent !important;\n}")) {
|
||||||
|
theme.CustomCSS = theme.CustomCSS + "\n" + migrationCSS;
|
||||||
|
await localforage.setItem(neumorphicThemeId, theme);
|
||||||
|
}
|
||||||
|
|
||||||
const themeCreatorOpen = localStorage.getItem("themeCreatorOpen");
|
const themeCreatorOpen = localStorage.getItem("themeCreatorOpen");
|
||||||
if (themeCreatorOpen === "true") {
|
if (themeCreatorOpen === "true") {
|
||||||
console.debug(
|
console.debug(
|
||||||
"[ThemeManager] Theme creator was open, clearing preview state",
|
"[ThemeManager] Theme creator was open, clearing preview state",
|
||||||
);
|
);
|
||||||
this.clearPreview();
|
this.clearPreview();
|
||||||
// Clean up the flag
|
|
||||||
localStorage.removeItem("themeCreatorOpen");
|
localStorage.removeItem("themeCreatorOpen");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import { updateAllColors } from "@/seqta/ui/colors/Manager";
|
|||||||
import loading from "@/seqta/ui/Loading";
|
import loading from "@/seqta/ui/Loading";
|
||||||
import { SendNewsPage } from "@/seqta/utils/SendNewsPage";
|
import { SendNewsPage } from "@/seqta/utils/SendNewsPage";
|
||||||
import { loadHomePage } from "@/seqta/utils/Loaders/LoadHomePage";
|
import { loadHomePage } from "@/seqta/utils/Loaders/LoadHomePage";
|
||||||
import { OpenWhatsNewPopup } from "@/seqta/utils/Whatsnew";
|
import { OpenWhatsNewPopup } from "@/seqta/utils/Openers/OpenWhatsNewPopup";
|
||||||
//import { OpenMinecraftServerPopup } from "@/seqta/utils/AboutMinecraftServer";
|
import { showPrivacyNotification } from "@/seqta/utils/Openers/OpenPrivacyNotification";
|
||||||
|
|
||||||
import {
|
import { updateTimetableTimes } from "@/seqta/utils/updateTimetableTimes";
|
||||||
updateTimetableTimes,
|
|
||||||
} from "@/seqta/utils/updateTimetableTimes";
|
|
||||||
|
|
||||||
// JSON content
|
// JSON content
|
||||||
import MenuitemSVGKey from "@/seqta/content/MenuItemSVGKey.json";
|
import MenuitemSVGKey from "@/seqta/content/MenuItemSVGKey.json";
|
||||||
@@ -96,7 +94,12 @@ export async function finishLoad() {
|
|||||||
console.error("Error during loading cleanup:", err);
|
console.error("Error during loading cleanup:", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsState.justupdated && !document.getElementById("whatsnewbk")) {
|
// Check and show privacy statement notification (before what's new)
|
||||||
|
if (!document.getElementById("privacy-notification")) {
|
||||||
|
await showPrivacyNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settingsState.justupdated && !document.getElementById("whatsnewbk") && !document.getElementById("privacy-notification")) {
|
||||||
OpenWhatsNewPopup();
|
OpenWhatsNewPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,28 +173,35 @@ async function updateStudentInfo(students: any) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
let houseelement1 = document.getElementsByClassName("userInfohouse")[0];
|
const houseelement = document.getElementsByClassName("userInfohouse")[0] as HTMLElement;
|
||||||
const houseelement = houseelement1 as HTMLElement;
|
|
||||||
|
// Fallback to N/A
|
||||||
|
let text = 'N/A';
|
||||||
|
const student = students[index] ?? {};
|
||||||
|
|
||||||
|
// If student has a house, prefer to show year + house. If no year, only show house.
|
||||||
|
if (student.house) {
|
||||||
|
text = `${student.year ?? ""}${student.house}`;
|
||||||
|
|
||||||
|
// If house_colour exists, compute colour
|
||||||
|
if (student.house_colour) {
|
||||||
|
houseelement.style.background = student.house_colour;
|
||||||
|
|
||||||
if (students[index]?.house) {
|
|
||||||
if (students[index]?.house_colour) {
|
|
||||||
houseelement.style.background = students[index].house_colour;
|
|
||||||
try {
|
try {
|
||||||
let colorresult = GetThresholdOfColor(students[index]?.house_colour);
|
const colorresult = GetThresholdOfColor(student.house_colour);
|
||||||
houseelement.style.color =
|
houseelement.style.color =
|
||||||
colorresult && colorresult > 300 ? "black" : "white";
|
colorresult && colorresult > 300 ? "black" : "white";
|
||||||
houseelement.innerText = students[index].year + students[index].house;
|
|
||||||
} catch (error) {
|
|
||||||
houseelement.innerText = students[index].house;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
houseelement.innerText = students[index].year;
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
houseelement.innerText = "N/A";
|
// Colour calculation failed, no text colour set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (student.year) {
|
||||||
|
// No house, only year will be shown
|
||||||
|
text = student.year;
|
||||||
|
}
|
||||||
|
|
||||||
|
houseelement.innerText = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNewsButton(fragment: DocumentFragment, menu: HTMLElement) {
|
function createNewsButton(fragment: DocumentFragment, menu: HTMLElement) {
|
||||||
|
|||||||
@@ -1,25 +1,17 @@
|
|||||||
import stringToHTML from "../stringToHTML";
|
import stringToHTML from "../stringToHTML";
|
||||||
import { settingsState } from "../listeners/SettingsState";
|
import { settingsState } from "../listeners/SettingsState";
|
||||||
import { animate, stagger } from "motion";
|
import { openPopup } from "./PopupManager";
|
||||||
import { DeleteWhatsNew } from "../Whatsnew";
|
|
||||||
|
|
||||||
export function OpenAboutPage() {
|
export function OpenAboutPage() {
|
||||||
const background = document.createElement("div");
|
const header = stringToHTML(
|
||||||
background.id = "whatsnewbk";
|
|
||||||
background.classList.add("whatsnewBackground");
|
|
||||||
|
|
||||||
const container = document.createElement("div");
|
|
||||||
container.classList.add("whatsnewContainer");
|
|
||||||
|
|
||||||
var header: any = stringToHTML(
|
|
||||||
/* html */
|
/* html */
|
||||||
`<div class="whatsnewHeader">
|
`<div class="whatsnewHeader">
|
||||||
<h1>About</h1>
|
<h1>About</h1>
|
||||||
<p>About the extension</p>
|
<p>About the extension</p>
|
||||||
</div>`,
|
</div>`,
|
||||||
).firstChild;
|
).firstChild as HTMLElement;
|
||||||
|
|
||||||
let text = stringToHTML(/* html */ `
|
const text = stringToHTML(/* html */ `
|
||||||
<div class="whatsnewTextContainer" style="overflow-y: hidden;">
|
<div class="whatsnewTextContainer" style="overflow-y: hidden;">
|
||||||
<img src="${settingsState.DarkMode ? "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/branding/dark.jpg" : "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/branding/light.jpg"}" class="aboutImg" />
|
<img src="${settingsState.DarkMode ? "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/branding/dark.jpg" : "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/branding/light.jpg"}" class="aboutImg" />
|
||||||
<p>BetterSEQTA+ is a fork of BetterSEQTA (originally developed by Nulkem), which was discontinued. BetterSEQTA+ continued development of BetterSEQTA, while incorporating a plethora of features. </p>
|
<p>BetterSEQTA+ is a fork of BetterSEQTA (originally developed by Nulkem), which was discontinued. BetterSEQTA+ continued development of BetterSEQTA, while incorporating a plethora of features. </p>
|
||||||
@@ -37,9 +29,9 @@ export function OpenAboutPage() {
|
|||||||
style="width: 100%; max-width: 500px; height: auto; object-fit: contain; display: block; margin: -110px auto 0;">
|
style="width: 100%; max-width: 500px; height: auto; object-fit: contain; display: block; margin: -110px auto 0;">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).firstChild;
|
`).firstChild as HTMLElement;
|
||||||
|
|
||||||
let footer = stringToHTML(/* html */ `
|
const footer = stringToHTML(/* html */ `
|
||||||
<div class="whatsnewFooter">
|
<div class="whatsnewFooter">
|
||||||
<div>
|
<div>
|
||||||
Resources and Feedback:
|
Resources and Feedback:
|
||||||
@@ -67,56 +59,10 @@ export function OpenAboutPage() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).firstChild;
|
`).firstChild as HTMLElement;
|
||||||
|
|
||||||
let exitbutton = document.createElement("div");
|
openPopup({
|
||||||
exitbutton.id = "whatsnewclosebutton";
|
header,
|
||||||
|
content: [text, footer],
|
||||||
container.append(header);
|
|
||||||
container.append(text as ChildNode);
|
|
||||||
container.append(footer as ChildNode);
|
|
||||||
container.append(exitbutton);
|
|
||||||
|
|
||||||
background.append(container);
|
|
||||||
|
|
||||||
document.getElementById("container")!.append(background);
|
|
||||||
|
|
||||||
let bkelement = document.getElementById("whatsnewbk");
|
|
||||||
let popup = document.getElementsByClassName("whatsnewContainer")[0];
|
|
||||||
|
|
||||||
if (settingsState.animations) {
|
|
||||||
animate(
|
|
||||||
[popup, bkelement as HTMLElement],
|
|
||||||
{ scale: [0, 1] },
|
|
||||||
{
|
|
||||||
type: "spring",
|
|
||||||
stiffness: 220,
|
|
||||||
damping: 18,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
animate(
|
|
||||||
".whatsnewTextContainer *",
|
|
||||||
{ opacity: [0, 1], y: [10, 0] },
|
|
||||||
{
|
|
||||||
delay: stagger(0.05, { startDelay: 0.1 }),
|
|
||||||
duration: 0.5,
|
|
||||||
ease: [0.22, 0.03, 0.26, 1],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete settingsState.justupdated;
|
|
||||||
|
|
||||||
bkelement!.addEventListener("click", function (event) {
|
|
||||||
// Check if the click event originated from the element itself and not any of its children
|
|
||||||
if (event.target === bkelement) {
|
|
||||||
DeleteWhatsNew();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var closeelement = document.getElementById("whatsnewclosebutton");
|
|
||||||
closeelement!.addEventListener("click", function () {
|
|
||||||
DeleteWhatsNew();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-98
@@ -1,24 +1,5 @@
|
|||||||
import { settingsState } from "./listeners/SettingsState";
|
import stringToHTML from "../stringToHTML";
|
||||||
import { animate, stagger } from "motion";
|
import { openPopup } from "./PopupManager";
|
||||||
import stringToHTML from "./stringToHTML";
|
|
||||||
|
|
||||||
export async function DeleteWhatsNew() {
|
|
||||||
const bkelement = document.getElementById("whatsnewbk");
|
|
||||||
const popup = document.querySelector(".whatsnewContainer") as HTMLElement;
|
|
||||||
|
|
||||||
if (!settingsState.animations) {
|
|
||||||
bkelement?.remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
animate(
|
|
||||||
[popup, bkelement!],
|
|
||||||
{ opacity: [1, 0], scale: [1, 0] },
|
|
||||||
{ ease: [0.22, 0.03, 0.26, 1] },
|
|
||||||
).then(() => {
|
|
||||||
bkelement?.remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OpenMinecraftServerPopup() {
|
export function OpenMinecraftServerPopup() {
|
||||||
if (!document.querySelector('link[href*="minecraftia"]')) {
|
if (!document.querySelector('link[href*="minecraftia"]')) {
|
||||||
@@ -28,45 +9,36 @@ export function OpenMinecraftServerPopup() {
|
|||||||
document.head.appendChild(fontLink);
|
document.head.appendChild(fontLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
const background = document.createElement("div");
|
const header = stringToHTML(
|
||||||
background.id = "whatsnewbk";
|
|
||||||
background.classList.add("whatsnewBackground");
|
|
||||||
|
|
||||||
const container = document.createElement("div");
|
|
||||||
container.classList.add("whatsnewContainer");
|
|
||||||
|
|
||||||
var header: any = stringToHTML(
|
|
||||||
/* html */
|
/* html */
|
||||||
`<div class="whatsnewHeader">
|
`<div class="whatsnewHeader">
|
||||||
<h1>Minecraft Server</h1>
|
<h1>Minecraft Server</h1>
|
||||||
<p>The official BetterSEQTA+ Minecraft Server</p>
|
<p>The official BetterSEQTA+ Minecraft Server</p>
|
||||||
</div>`,
|
</div>`,
|
||||||
).firstChild;
|
).firstChild as HTMLElement;
|
||||||
|
|
||||||
let imagecont = document.createElement("div");
|
const imageContainer = document.createElement("div");
|
||||||
imagecont.classList.add("whatsnewImgContainer");
|
imageContainer.classList.add("whatsnewImgContainer");
|
||||||
|
|
||||||
let video = document.createElement("video");
|
const video = document.createElement("video");
|
||||||
video.style.aspectRatio = "16/9";
|
video.style.aspectRatio = "16/9";
|
||||||
video.style.background = "black";
|
video.style.background = "black";
|
||||||
let source = document.createElement("source");
|
|
||||||
|
|
||||||
|
const source = document.createElement("source");
|
||||||
source.setAttribute(
|
source.setAttribute(
|
||||||
"src",
|
"src",
|
||||||
"https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/server-video.mp4",
|
"https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/server-video.mp4",
|
||||||
);
|
);
|
||||||
|
|
||||||
video.autoplay = true;
|
video.autoplay = true;
|
||||||
video.muted = true;
|
video.muted = true;
|
||||||
video.loop = true;
|
video.loop = true;
|
||||||
video.appendChild(source);
|
video.appendChild(source);
|
||||||
video.classList.add("whatsnewImg");
|
video.classList.add("whatsnewImg");
|
||||||
imagecont.appendChild(video);
|
imageContainer.appendChild(video);
|
||||||
|
|
||||||
let textcontainer = document.createElement("div");
|
const text = stringToHTML(/* html */ `
|
||||||
textcontainer.classList.add("whatsnewTextContainer");
|
<div class="whatsnewTextContainer" style="height: 50%; overflow-y: hidden;">
|
||||||
|
|
||||||
let text = stringToHTML(/* html */ `
|
|
||||||
<div class="whatsnewTextContainer" style="height: 50%; overflow-y: scroll;">
|
|
||||||
<h1>Join our community in Minecraft!</h1>
|
<h1>Join our community in Minecraft!</h1>
|
||||||
<p style="margin-left: 0;">Join the official BetterSEQTA+ Minecraft Server community now!</p>
|
<p style="margin-left: 0;">Join the official BetterSEQTA+ Minecraft Server community now!</p>
|
||||||
|
|
||||||
@@ -92,8 +64,7 @@ export function OpenMinecraftServerPopup() {
|
|||||||
-1px -1px 0 #000,
|
-1px -1px 0 #000,
|
||||||
1px -1px 0 #000,
|
1px -1px 0 #000,
|
||||||
-1px 1px 0 #000,
|
-1px 1px 0 #000,
|
||||||
1px 1px 0 #000;
|
1px 1px 0 #000;">
|
||||||
">
|
|
||||||
mc.betterseqta.org
|
mc.betterseqta.org
|
||||||
</p>
|
</p>
|
||||||
<p style="
|
<p style="
|
||||||
@@ -107,14 +78,13 @@ export function OpenMinecraftServerPopup() {
|
|||||||
-1px -1px 0 #000,
|
-1px -1px 0 #000,
|
||||||
1px -1px 0 #000,
|
1px -1px 0 #000,
|
||||||
-1px 1px 0 #000,
|
-1px 1px 0 #000,
|
||||||
1px 1px 0 #000;
|
1px 1px 0 #000;">
|
||||||
">
|
|
||||||
Version: 1.21.4
|
Version: 1.21.4
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`).firstChild;
|
`).firstChild as HTMLElement;
|
||||||
|
|
||||||
let footer = stringToHTML(/* html */ `
|
const footer = stringToHTML(/* html */ `
|
||||||
<div class="whatsnewFooter">
|
<div class="whatsnewFooter">
|
||||||
<div>
|
<div>
|
||||||
Resources and Feedback:
|
Resources and Feedback:
|
||||||
@@ -144,59 +114,10 @@ export function OpenMinecraftServerPopup() {
|
|||||||
<div>
|
<div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).firstChild;
|
`).firstChild as HTMLElement;
|
||||||
|
|
||||||
let exitbutton = document.createElement("div");
|
openPopup({
|
||||||
exitbutton.id = "whatsnewclosebutton";
|
|
||||||
|
|
||||||
container.append(
|
|
||||||
header,
|
header,
|
||||||
imagecont,
|
content: [imageContainer, text, footer],
|
||||||
text as HTMLElement,
|
|
||||||
footer as HTMLElement,
|
|
||||||
exitbutton,
|
|
||||||
);
|
|
||||||
|
|
||||||
background.append(container);
|
|
||||||
|
|
||||||
document.getElementById("container")!.append(background);
|
|
||||||
|
|
||||||
let bkelement = document.getElementById("whatsnewbk");
|
|
||||||
let popup = document.getElementsByClassName("whatsnewContainer")[0];
|
|
||||||
|
|
||||||
if (settingsState.animations) {
|
|
||||||
animate(
|
|
||||||
[popup, bkelement as HTMLElement],
|
|
||||||
{ scale: [0, 1] },
|
|
||||||
{
|
|
||||||
type: "spring",
|
|
||||||
stiffness: 220,
|
|
||||||
damping: 18,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
animate(
|
|
||||||
".whatsnewTextContainer *",
|
|
||||||
{ opacity: [0, 1], y: [10, 0] },
|
|
||||||
{
|
|
||||||
delay: stagger(0.05, { startDelay: 0.1 }),
|
|
||||||
duration: 0.5,
|
|
||||||
ease: [0.22, 0.03, 0.26, 1],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete settingsState.justupdated;
|
|
||||||
|
|
||||||
bkelement!.addEventListener("click", function (event) {
|
|
||||||
// Check if the click event originated from the element itself and not any of its children
|
|
||||||
if (event.target === bkelement) {
|
|
||||||
DeleteWhatsNew();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var closeelement = document.getElementById("whatsnewclosebutton");
|
|
||||||
closeelement!.addEventListener("click", function () {
|
|
||||||
DeleteWhatsNew();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import stringToHTML from "../stringToHTML";
|
||||||
|
import { settingsState } from "../listeners/SettingsState";
|
||||||
|
import { openPopup } from "./PopupManager";
|
||||||
|
|
||||||
|
export function showPrivacyNotification() {
|
||||||
|
const lastUpdated = "2025-12-19";
|
||||||
|
|
||||||
|
if (document.getElementById("whatsnewbk")) return;
|
||||||
|
if (settingsState.privacyStatementShown) return;
|
||||||
|
if (settingsState.privacyStatementLastUpdated && new Date(settingsState.privacyStatementLastUpdated) > new Date(lastUpdated)) return;
|
||||||
|
|
||||||
|
const header = stringToHTML(
|
||||||
|
/* html */
|
||||||
|
`<div class="whatsnewHeader">
|
||||||
|
<h1>Privacy Statement</h1>
|
||||||
|
<p>Important Information</p>
|
||||||
|
</div>`,
|
||||||
|
).firstChild as HTMLElement;
|
||||||
|
|
||||||
|
const text = stringToHTML(/* html */ `
|
||||||
|
<div class="whatsnewTextContainer privacyStatement" style="overflow-y: auto; font-size: 1.2rem; line-height: 1.6;">
|
||||||
|
<img style="aspect-ratio: 16/5.8;" src="${settingsState.DarkMode ? "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/branding/dark.jpg" : "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/branding/light.jpg"}" class="aboutImg" />
|
||||||
|
<p>
|
||||||
|
<strong>Addressing Recent Concerns About BetterSEQTA+</strong><br>
|
||||||
|
We appreciate the feedback we've received from several schools regarding BetterSEQTA+. Transparency and trust are core to our mission, and we want to address these concerns directly.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Our Commitment to Privacy:</strong><br>
|
||||||
|
<span style="display: block; margin-left: 1em;">
|
||||||
|
• We do not collect, store, or share any personal information<br>
|
||||||
|
• All data processing happens locally on your device<br>
|
||||||
|
• Our code is open source and available for review
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>What We're Doing:</strong><br>
|
||||||
|
We're willing to actively work with school administrators to ensure BetterSEQTA+ meets both student needs and institutional requirements. If your school has specific concerns, we encourage them to contact us at <a href="mailto:betterseqta.plus@gmail.com" style="color: inherit; text-decoration: underline;">betterseqta.plus@gmail.com</a> or via github at <a href="https://github.com/BetterSEQTA/BetterSEQTA-Plus" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">github.com/BetterSEQTA/BetterSEQTA-Plus</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For complete details about our privacy practices, visit our <a href="https://betterseqta.org/privacy" target="_blank" rel="noopener noreferrer" style="color: inherit; text-decoration: underline;">privacy policy</a> or click the shield icon in settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`).firstChild as HTMLElement;
|
||||||
|
|
||||||
|
settingsState.privacyStatementLastUpdated = "2025-12-20";
|
||||||
|
settingsState.privacyStatementShown = true;
|
||||||
|
|
||||||
|
openPopup({
|
||||||
|
header,
|
||||||
|
content: [text],
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import stringToHTML from "../stringToHTML";
|
||||||
|
import { openPopup } from "./PopupManager";
|
||||||
|
|
||||||
|
export function OpenPrivacyStatement() {
|
||||||
|
const header = stringToHTML(
|
||||||
|
/* html */
|
||||||
|
`<div class="whatsnewHeader">
|
||||||
|
<h1>Privacy Statement</h1>
|
||||||
|
<p>Our commitment to your privacy</p>
|
||||||
|
</div>`,
|
||||||
|
).firstChild as HTMLElement;
|
||||||
|
|
||||||
|
const text = stringToHTML(/* html */ `
|
||||||
|
<div class="whatsnewTextContainer" style="overflow-y: auto; max-height: 60vh;">
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<h3>Data Collection</h3>
|
||||||
|
<p><strong>We never collect any information from you.</strong> BetterSEQTA+ is designed to work entirely on your device. All processing happens locally in your browser, and we do not send any data to external servers.</p>
|
||||||
|
|
||||||
|
<h3>What We Don't Do</h3>
|
||||||
|
<ul style="text-align: left; margin: 10px 0;">
|
||||||
|
<li>We do not track your browsing activity</li>
|
||||||
|
<li>We do not collect personal information</li>
|
||||||
|
<li>We do not store your SEQTA credentials</li>
|
||||||
|
<li>We do not send data to third-party services</li>
|
||||||
|
<li>We do not use analytics or tracking cookies</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Local Storage</h3>
|
||||||
|
<p>BetterSEQTA+ uses your browser's local storage to save your preferences and settings. This data remains on your device and is never transmitted anywhere. You can clear this data at any time through your browser's settings.</p>
|
||||||
|
|
||||||
|
<h3>Open Source</h3>
|
||||||
|
<p>BetterSEQTA+ is an open-source project. You can review our code on <a href="https://github.com/BetterSEQTA/BetterSEQTA-Plus" target="_blank" style="color: inherit; text-decoration: underline;">GitHub</a> to verify our privacy practices. We believe in transparency and encourage you to inspect the code yourself.</p>
|
||||||
|
|
||||||
|
<h3>Our Commitment</h3>
|
||||||
|
<p>We are committed to providing the best features possible while respecting your privacy. We understand that schools and students have concerns about data privacy, and we want to assure you that BetterSEQTA+ is designed with privacy as a core principle.</p>
|
||||||
|
|
||||||
|
<p style="margin-top: 20px; font-weight: bold;">If you have any questions or concerns about our privacy practices, please reach out to us through our <a href="https://github.com/BetterSEQTA/BetterSEQTA-Plus" target="_blank" style="color: inherit; text-decoration: underline;">GitHub repository</a>.</p>
|
||||||
|
</div>
|
||||||
|
`).firstChild as HTMLElement;
|
||||||
|
|
||||||
|
openPopup({
|
||||||
|
header,
|
||||||
|
content: [text],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,48 +1,22 @@
|
|||||||
import { settingsState } from "./listeners/SettingsState";
|
import stringToHTML from "../stringToHTML";
|
||||||
import { animate, stagger } from "motion";
|
|
||||||
import stringToHTML from "./stringToHTML";
|
|
||||||
import browser from "webextension-polyfill";
|
import browser from "webextension-polyfill";
|
||||||
import kofi from "@/resources/kofi.png?base64";
|
import kofi from "@/resources/kofi.png?base64";
|
||||||
|
import { openPopup } from "./PopupManager";
|
||||||
export async function DeleteWhatsNew() {
|
|
||||||
const bkelement = document.getElementById("whatsnewbk");
|
|
||||||
const popup = document.getElementsByClassName("whatsnewContainer")[0];
|
|
||||||
|
|
||||||
if (!settingsState.animations) {
|
|
||||||
bkelement?.remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
animate(
|
|
||||||
[popup, bkelement!],
|
|
||||||
{ opacity: [1, 0], scale: [1, 0] },
|
|
||||||
{ ease: [0.22, 0.03, 0.26, 1] },
|
|
||||||
).then(() => {
|
|
||||||
bkelement?.remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function OpenWhatsNewPopup() {
|
export function OpenWhatsNewPopup() {
|
||||||
const background = document.createElement("div");
|
const header = stringToHTML(
|
||||||
background.id = "whatsnewbk";
|
|
||||||
background.classList.add("whatsnewBackground");
|
|
||||||
|
|
||||||
const container = document.createElement("div");
|
|
||||||
container.classList.add("whatsnewContainer");
|
|
||||||
|
|
||||||
var header: any = stringToHTML(
|
|
||||||
/* html */
|
/* html */
|
||||||
`<div class="whatsnewHeader">
|
`<div class="whatsnewHeader">
|
||||||
<h1>What's New</h1>
|
<h1>What's New</h1>
|
||||||
<p>BetterSEQTA+ V${browser.runtime.getManifest().version}</p>
|
<p>BetterSEQTA+ V${browser.runtime.getManifest().version}</p>
|
||||||
</div>`,
|
</div>`,
|
||||||
).firstChild;
|
).firstChild as HTMLElement;
|
||||||
|
|
||||||
let imagecont = document.createElement("div");
|
const imageContainer = document.createElement("div");
|
||||||
imagecont.classList.add("whatsnewImgContainer");
|
imageContainer.classList.add("whatsnewImgContainer");
|
||||||
|
|
||||||
let video = document.createElement("video");
|
const video = document.createElement("video");
|
||||||
let source = document.createElement("source");
|
const source = document.createElement("source");
|
||||||
|
|
||||||
source.setAttribute(
|
source.setAttribute(
|
||||||
"src",
|
"src",
|
||||||
@@ -53,19 +27,23 @@ export function OpenWhatsNewPopup() {
|
|||||||
video.loop = true;
|
video.loop = true;
|
||||||
video.appendChild(source);
|
video.appendChild(source);
|
||||||
video.classList.add("whatsnewImg");
|
video.classList.add("whatsnewImg");
|
||||||
imagecont.appendChild(video);
|
imageContainer.appendChild(video);
|
||||||
|
|
||||||
/* let whatsnewimg = document.createElement("img");
|
const text = stringToHTML(/* html */ `
|
||||||
//whatsnewimg.src = "https://raw.githubusercontent.com/BetterSEQTA/BetterSEQTA-Plus/main/src/resources/update-image.webp";
|
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: auto;">
|
||||||
whatsnewimg.src = browser.runtime.getURL('../../resources/update-image.webp');
|
|
||||||
whatsnewimg.classList.add("whatsnewImg");
|
|
||||||
imagecont.appendChild(whatsnewimg); */
|
|
||||||
|
|
||||||
let textcontainer = document.createElement("div");
|
<h1>3.4.13 - Bug Fixes & Styling Improvements</h1>
|
||||||
textcontainer.classList.add("whatsnewTextContainer");
|
<li>Fixed house/year box hard failing when house_colour does not exist</li>
|
||||||
|
<li>Fixed message of the day being unreadable in light mode</li>
|
||||||
|
<li>Fixed global font styling issues due to SEQTA updates</li>
|
||||||
|
<li>Fixed styling issues with title bar and other elements</li>
|
||||||
|
<li>Other minor bug fixes and improvements</li>
|
||||||
|
|
||||||
let text = stringToHTML(/* html */ `
|
<h1>3.4.12 - Privacy Updates & Bug Fixes</h1>
|
||||||
<div class="whatsnewTextContainer" style="height: 50%;overflow-y: scroll;">
|
<li>Added privacy statement</li>
|
||||||
|
<li>Added disclaimer modal to assessment averages switch</li>
|
||||||
|
<li>Improved popup management system</li>
|
||||||
|
<li>Other minor bug fixes and improvements</li>
|
||||||
|
|
||||||
<h1>3.4.11 - New Features & Bug Fixes</h1>
|
<h1>3.4.11 - New Features & Bug Fixes</h1>
|
||||||
<li>Added Background Music plugin</li>
|
<li>Added Background Music plugin</li>
|
||||||
@@ -291,9 +269,9 @@ export function OpenWhatsNewPopup() {
|
|||||||
<h1>Create Custom Shortcuts</h1>
|
<h1>Create Custom Shortcuts</h1>
|
||||||
<li>Found in the BetterSEQTA+ Settings menu, custom shortcuts can now be created with a name and URL of your choice.</li>
|
<li>Found in the BetterSEQTA+ Settings menu, custom shortcuts can now be created with a name and URL of your choice.</li>
|
||||||
</div>
|
</div>
|
||||||
`).firstChild;
|
`).firstChild as HTMLElement;
|
||||||
|
|
||||||
let footer = stringToHTML(/* html */ `
|
const footer = stringToHTML(/* html */ `
|
||||||
<div class="whatsnewFooter">
|
<div class="whatsnewFooter">
|
||||||
<div>
|
<div>
|
||||||
Resources and Feedback:
|
Resources and Feedback:
|
||||||
@@ -326,58 +304,10 @@ export function OpenWhatsNewPopup() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).firstChild;
|
`).firstChild as HTMLElement;
|
||||||
|
|
||||||
let exitbutton = document.createElement("div");
|
openPopup({
|
||||||
exitbutton.id = "whatsnewclosebutton";
|
header,
|
||||||
|
content: [imageContainer, text, footer],
|
||||||
container.append(header);
|
|
||||||
container.append(imagecont);
|
|
||||||
container.append(textcontainer);
|
|
||||||
container.append(text as ChildNode);
|
|
||||||
container.append(footer as ChildNode);
|
|
||||||
container.append(exitbutton);
|
|
||||||
|
|
||||||
background.append(container);
|
|
||||||
|
|
||||||
document.getElementById("container")!.append(background);
|
|
||||||
|
|
||||||
let bkelement = document.getElementById("whatsnewbk");
|
|
||||||
let popup = document.getElementsByClassName("whatsnewContainer")[0];
|
|
||||||
|
|
||||||
if (settingsState.animations) {
|
|
||||||
animate(
|
|
||||||
[popup, bkelement as HTMLElement],
|
|
||||||
{ scale: [0, 1] },
|
|
||||||
{
|
|
||||||
type: "spring",
|
|
||||||
stiffness: 220,
|
|
||||||
damping: 18,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
animate(
|
|
||||||
".whatsnewTextContainer *",
|
|
||||||
{ opacity: [0, 1], y: [10, 0] },
|
|
||||||
{
|
|
||||||
delay: stagger(0.05, { startDelay: 0.1 }),
|
|
||||||
duration: 0.5,
|
|
||||||
ease: [0.22, 0.03, 0.26, 1],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete settingsState.justupdated;
|
|
||||||
|
|
||||||
bkelement!.addEventListener("click", function (event) {
|
|
||||||
// Check if the click event originated from the element itself and not any of its children
|
|
||||||
if (event.target === bkelement) {
|
|
||||||
DeleteWhatsNew();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var closeelement = document.getElementById("whatsnewclosebutton");
|
|
||||||
closeelement!.addEventListener("click", function () {
|
|
||||||
DeleteWhatsNew();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { settingsState } from "../listeners/SettingsState";
|
||||||
|
import { animate as motionAnimate, stagger } from "motion";
|
||||||
|
|
||||||
|
type AnimationTarget = string | Element | Element[] | NodeList | null;
|
||||||
|
|
||||||
|
let isClosing = false;
|
||||||
|
|
||||||
|
export async function closePopup() {
|
||||||
|
if (isClosing) return;
|
||||||
|
isClosing = true;
|
||||||
|
|
||||||
|
const background = document.getElementById("whatsnewbk");
|
||||||
|
const popup = document.getElementsByClassName("whatsnewContainer")[0] as
|
||||||
|
| HTMLElement
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (!background || !popup) {
|
||||||
|
isClosing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settingsState.animations) {
|
||||||
|
background.remove();
|
||||||
|
isClosing = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await (motionAnimate as any)(
|
||||||
|
[popup, background],
|
||||||
|
{ opacity: [1, 0], scale: [1, 0.95] },
|
||||||
|
{ duration: 0.25, easing: [0.22, 0.03, 0.26, 1] },
|
||||||
|
);
|
||||||
|
|
||||||
|
background.remove();
|
||||||
|
isClosing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OpenPopupOptions {
|
||||||
|
header?: Node | null;
|
||||||
|
content?: (Node | null | undefined)[];
|
||||||
|
animateSelector?: AnimationTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openPopup({
|
||||||
|
header,
|
||||||
|
content = [],
|
||||||
|
animateSelector = ".whatsnewTextContainer *",
|
||||||
|
}: OpenPopupOptions = {}) {
|
||||||
|
const background = document.createElement("div");
|
||||||
|
background.id = "whatsnewbk";
|
||||||
|
background.classList.add("whatsnewBackground");
|
||||||
|
|
||||||
|
const container = document.createElement("div");
|
||||||
|
container.classList.add("whatsnewContainer");
|
||||||
|
|
||||||
|
if (header) container.append(header);
|
||||||
|
for (const node of content) if (node) container.append(node);
|
||||||
|
|
||||||
|
const closeButton = document.createElement("div");
|
||||||
|
closeButton.id = "whatsnewclosebutton";
|
||||||
|
container.append(closeButton);
|
||||||
|
|
||||||
|
background.append(container);
|
||||||
|
document.getElementById("container")!.append(background);
|
||||||
|
|
||||||
|
if (settingsState.animations) {
|
||||||
|
(motionAnimate as any)(
|
||||||
|
[container, background],
|
||||||
|
{ scale: [0, 1] },
|
||||||
|
{ type: "spring", stiffness: 220, damping: 18 },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (animateSelector) {
|
||||||
|
const targets =
|
||||||
|
typeof animateSelector === "string"
|
||||||
|
? document.querySelectorAll(animateSelector)
|
||||||
|
: animateSelector;
|
||||||
|
|
||||||
|
(motionAnimate as any)(
|
||||||
|
targets!,
|
||||||
|
{ opacity: [0, 1], y: [10, 0] },
|
||||||
|
{
|
||||||
|
delay: stagger(0.05, { startDelay: 0.1 }),
|
||||||
|
duration: 0.5,
|
||||||
|
easing: [0.22, 0.03, 0.26, 1],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete settingsState.justupdated;
|
||||||
|
|
||||||
|
background.addEventListener("click", (event) => {
|
||||||
|
if (event.target === background) void closePopup();
|
||||||
|
});
|
||||||
|
|
||||||
|
closeButton.addEventListener("click", () => void closePopup());
|
||||||
|
}
|
||||||
@@ -30,6 +30,8 @@ export interface SettingsState {
|
|||||||
subjectfilters: Record<string, any>;
|
subjectfilters: Record<string, any>;
|
||||||
transparencyEffects: boolean;
|
transparencyEffects: boolean;
|
||||||
justupdated?: boolean;
|
justupdated?: boolean;
|
||||||
|
privacyStatementShown?: boolean;
|
||||||
|
privacyStatementLastUpdated?: string;
|
||||||
timeFormat?: string;
|
timeFormat?: string;
|
||||||
animations: boolean;
|
animations: boolean;
|
||||||
defaultPage: string;
|
defaultPage: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user