schoolbox web extension :)
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: update badge icons and update notification badge

willow 04bae75e 8f5163f3

+86 -66
public/icon/128-badge.png

This is a binary file and will not be displayed.

public/icon/128-ctp-badge.png

This is a binary file and will not be displayed.

public/icon/128-ctp-disabled-badge.png

This is a binary file and will not be displayed.

public/icon/128-ctp-disabled.png

This is a binary file and will not be displayed.

public/icon/128-ctp.png

This is a binary file and will not be displayed.

public/icon/128-disabled-badge.png

This is a binary file and will not be displayed.

public/icon/128-disabled.png

This is a binary file and will not be displayed.

public/icon/128-green.png

This is a binary file and will not be displayed.

public/icon/128-red.png

This is a binary file and will not be displayed.

public/icon/128.png

This is a binary file and will not be displayed.

public/icon/16-badge.png

This is a binary file and will not be displayed.

public/icon/16-ctp-badge.png

This is a binary file and will not be displayed.

public/icon/16-ctp-disabled-badge.png

This is a binary file and will not be displayed.

public/icon/16-ctp-disabled.png

This is a binary file and will not be displayed.

public/icon/16-ctp.png

This is a binary file and will not be displayed.

public/icon/16-disabled-badge.png

This is a binary file and will not be displayed.

public/icon/16-disabled.png

This is a binary file and will not be displayed.

public/icon/16-green.png

This is a binary file and will not be displayed.

public/icon/16-red.png

This is a binary file and will not be displayed.

public/icon/16.png

This is a binary file and will not be displayed.

public/icon/32-badge.png

This is a binary file and will not be displayed.

public/icon/32-ctp-badge.png

This is a binary file and will not be displayed.

public/icon/32-ctp-disabled-badge.png

This is a binary file and will not be displayed.

public/icon/32-ctp-disabled.png

This is a binary file and will not be displayed.

public/icon/32-ctp.png

This is a binary file and will not be displayed.

public/icon/32-disabled-badge.png

This is a binary file and will not be displayed.

public/icon/32-disabled.png

This is a binary file and will not be displayed.

public/icon/32-green.png

This is a binary file and will not be displayed.

public/icon/32-red.png

This is a binary file and will not be displayed.

public/icon/32.png

This is a binary file and will not be displayed.

public/icon/48-badge.png

This is a binary file and will not be displayed.

public/icon/48-ctp-badge.png

This is a binary file and will not be displayed.

public/icon/48-ctp-disabled-badge.png

This is a binary file and will not be displayed.

public/icon/48-ctp-disabled.png

This is a binary file and will not be displayed.

public/icon/48-ctp.png

This is a binary file and will not be displayed.

public/icon/48-disabled-badge.png

This is a binary file and will not be displayed.

public/icon/48-disabled.png

This is a binary file and will not be displayed.

public/icon/48-green.png

This is a binary file and will not be displayed.

public/icon/48-red.png

This is a binary file and will not be displayed.

public/icon/48.png

This is a binary file and will not be displayed.

+32 -34
src/entrypoints/background.ts
··· 9 9 browser.tabs.create({ url: browser.runtime.getURL("/popup.html") }); 10 10 } 11 11 } else if (reason === "update") { 12 - logger.info("[background] Notifying user about the update"); 13 - browser.notifications.create("updated", { 14 - title: "Schooltape updated", 15 - type: "basic", 16 - iconUrl: browser.runtime.getURL("/icon/128.png"), 17 - message: "Click here to look at the release notes.", 18 - }); 12 + logger.info("[background] Showing update badge"); 13 + 14 + await globalSettings.set({ updated: true }); 15 + updateIcon(); 16 + 19 17 if (import.meta.env.DEV) { 20 18 logger.info("[background] Opening development URLs"); 21 19 browser.tabs.create({ url: browser.runtime.getURL("/popup.html"), active: false }); ··· 23 21 } 24 22 }); 25 23 26 - browser.notifications.onClicked.addListener(function (notifID) { 27 - if (notifID === "update") { 28 - browser.tabs.create({ 29 - url: "https://github.com/schooltape/schooltape/releases/latest", 30 - }); 31 - } 32 - if (notifID === "updated") { 33 - browser.tabs.create({ 34 - url: `https://github.com/schooltape/schooltape/releases/tag/v${browser.runtime.getManifest().version}`, 35 - }); 36 - } 37 - }); 38 - 39 - // watch for global toggle 40 - globalSettings.storage.watch(async (newSettings, oldSettings) => { 41 - if (newSettings.global !== oldSettings.global) { 42 - logger.info(`[background] Global toggle changed to ${newSettings.global}`); 43 - // update icon 44 - updateIcon(); 45 - } 24 + // update icon when toggle or update is changed 25 + globalSettings.storage.watch(() => { 26 + updateIcon(); 46 27 }); 47 28 48 29 // listen for messages ··· 50 31 resetSettings?: boolean; 51 32 inject?: string; 52 33 toTab?: string; 34 + updateIcon?: boolean; 53 35 } 36 + 54 37 // eslint-disable-next-line @typescript-eslint/no-explicit-any 55 38 browser.runtime.onMessage.addListener(async (msg: any, sender: any) => { 56 39 const message = msg as Message; 57 40 logger.child({ message, sender }).info("[background] Received message"); 41 + 58 42 if (message.resetSettings) { 59 43 resetSettings(); 60 - } 61 - if (message.toTab) { 44 + } else if (message.toTab) { 62 45 const tabs = await browser.tabs.query({ url: message.toTab }); 63 46 if (tabs.length > 0) { 47 + // @ts-expect-error - tab will exist 64 48 browser.tabs.update(tabs[0].id, { active: true }); 65 49 } else if (sender.tab?.id) { 66 50 browser.tabs.update(sender.tab.id, { url: message.toTab }); 67 51 } 52 + } else if (message.updateIcon) { 53 + updateIcon(); 68 54 } 69 - return true; 55 + 56 + return true; // return success 70 57 }); 71 58 72 59 // context menus ··· 120 107 } 121 108 122 109 async function updateIcon() { 123 - const global = (await globalSettings.storage.getValue()).global; 124 - let iconSuffix = "-disabled"; 125 - if (global) { 126 - iconSuffix = ""; 110 + logger.info("[background] Updating icon..."); 111 + const settingsValue = await globalSettings.storage.getValue(); 112 + 113 + let iconSuffix = ""; 114 + 115 + // if it's june 116 + if (new Date().getMonth() === 5) { 117 + iconSuffix += "-ctp"; 118 + } 119 + if (settingsValue.global === false) { 120 + iconSuffix += "-disabled"; 121 + } 122 + if (settingsValue.updated === true) { 123 + iconSuffix += "-badge"; 127 124 } 125 + 128 126 if (import.meta.env.MANIFEST_VERSION === 2) { 129 127 browser.browserAction.setIcon({ 130 128 path: {
+5 -1
src/entrypoints/popup/App.svelte
··· 10 10 import { flavors } from "@catppuccin/palette"; 11 11 import { needsRefresh } from "@/utils/storage"; 12 12 import { globalSettings } from "#imports"; 13 + import { RotateCw } from "@lucide/svelte"; 13 14 14 15 const routes = { 15 16 "/": Home, ··· 47 48 <a href="#/themes" class="navbutton-center" use:active={{ className: "active" }}>Themes</a> 48 49 <a href="#/snippets" class="navbutton-right" use:active={{ className: "active" }}>Snippets</a> 49 50 </nav> 51 + 50 52 <Banner 53 + message="Click here to apply changes" 51 54 visible={needsRefresh.state} 52 55 onclick={() => { 53 56 needsRefresh.storage.setValue(false); 54 57 refreshSchoolboxURLs(); 55 - }} /> 58 + }}><RotateCw /></Banner> 59 + 56 60 <Router {routes} /> 57 61 </main>
+4 -6
src/entrypoints/popup/components/Banner.svelte
··· 1 1 <script lang="ts"> 2 - let { visible = false, onclick } = $props(); 3 - 4 - import { Info } from "@lucide/svelte"; 2 + let { children, message, onclick, visible = false } = $props(); 5 3 </script> 6 4 7 5 {#if visible} 8 6 <button 9 - class="banner bg-ctp-blue text-ctp-crust rounded-lg mb-4 flex items-center justify-center p-4 w-full" 7 + class="banner cursor-pointer flex gap-2 items-center justify-center bg-ctp-blue text-ctp-crust rounded-lg mb-4 p-4 w-full" 10 8 {onclick}> 11 - <Info class="w-8 mr-2" /> 12 - <p>Click here to apply changes</p> 9 + {@render children()} 10 + <p>{message}</p> 13 11 </button> 14 12 {/if}
+21 -7
src/entrypoints/popup/components/Footer.svelte
··· 1 1 <script lang="ts"> 2 2 import { onMount } from "svelte"; 3 3 import IconBtn from "./inputs/IconBtn.svelte"; 4 - import { MessageCircleMore, RotateCcw, BookText } from "@lucide/svelte"; 4 + import { MessageCircleMore, RotateCcw, BookText, GitBranch } from "@lucide/svelte"; 5 5 6 6 let version = $state(); 7 7 ··· 29 29 </script> 30 30 31 31 <footer class="flex min-w-full justify-around p-4 mt-4"> 32 - <p class="mb-0 flex items-center text-ctp-text"> 33 - <a 34 - class="version ml-2 text-ctp-subtext0 hover:underline" 35 - target="_blank" 36 - href="https://github.com/schooltape/schooltape/releases/tag/v{version}">Version: v{version}</a> 37 - </p> 32 + <span class="relative inline-flex"> 33 + <button 34 + onclick={async () => { 35 + await globalSettings.set({ updated: false }); 36 + 37 + browser.tabs.create({ 38 + url: `https://github.com/schooltape/schooltape/releases/tag/v${version}`, 39 + }); 40 + }} 41 + class="rounded-lg px-2 text-ctp-subtext0 hover:bg-ctp-surface1"> 42 + <span class="flex gap-2 items-center"><GitBranch size={18} />v{version}</span> 43 + <!-- show ripple badge if the extension has been updated (unread release notes) --> 44 + {#if globalSettings.state.updated} 45 + <span class="absolute top-0 right-0 -mt-1 -mr-1 flex size-3"> 46 + <span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-ctp-blue opacity-75"></span> 47 + <span class="relative inline-flex size-3 rounded-full bg-ctp-blue"></span> 48 + </span> 49 + {/if} 50 + </button> 51 + </span> 38 52 <div class="flex"> 39 53 <IconBtn title="Wiki" id="wiki" onclick={handleWikiClick}><BookText /></IconBtn> 40 54 <IconBtn title="Discord" id="discord" onclick={handleDiscordClick}><MessageCircleMore /></IconBtn>
+7 -12
src/entrypoints/popup/components/Motd.svelte
··· 1 - <script> 1 + <script lang="ts"> 2 2 import { onMount } from "svelte"; 3 3 4 - let motd = $state(); 5 - 6 4 onMount(async () => { 7 - // fetch MOTD from website 8 - try { 9 - const response = await fetch("https://schooltape.github.io/motd.txt"); 10 - if (response.ok) { 11 - motd = await response.text(); 12 - } 13 - } catch (e) { 14 - motd = ""; 5 + // update MOTD 6 + const response = await fetch("https://schooltape.github.io/motd.txt"); 7 + if (response.ok) { 8 + globalSettings.set({ motd: await response.text() }); 15 9 } 16 10 }); 17 11 </script> 18 12 19 13 <!-- MOTD --> 20 14 <div class="text-ctp-subtext0 text-center italic"> 21 - <p>{@html motd}</p> 15 + <!-- eslint-disable-next-line svelte/no-at-html-tags --> 16 + <p>{@html globalSettings.state.motd}</p> 22 17 <!-- Free and <a href='https://github.com/schooltape/schooltape' class='text-(--ctp-accent)'> open source</a>! --> 23 18 </div>
+5 -1
src/entrypoints/popup/components/inputs/IconBtn.svelte
··· 13 13 let { children, title, id, onclick, label = "" }: Props = $props(); 14 14 </script> 15 15 16 - <button {title} {id} class="flex items-center ml-4 small hover:text-ctp-crust hover:bg-(--ctp-accent)" {onclick}> 16 + <button 17 + {title} 18 + {id} 19 + class="flex items-center cursor-pointer ml-4 small hover:text-ctp-crust hover:bg-(--ctp-accent)" 20 + {onclick}> 17 21 {@render children()} 18 22 {#if label} 19 23 <span class="ml-3">{label}</span>
+1 -1
src/entrypoints/popup/components/inputs/Toggle.svelte
··· 14 14 let { update, checked, id, size = "big", text = "", description = "", children }: Props = $props(); 15 15 </script> 16 16 17 - <label class="group text-ctp-text relative flex items-center justify-between py-2"> 17 + <label class="group text-ctp-text relative flex items-center justify-between py-2 cursor-pointer"> 18 18 <h4 class="text-ctp-text">{text}</h4> 19 19 <input 20 20 {id}
+3
src/entrypoints/start.content.ts
··· 23 23 if (settings.snippets) { 24 24 injectUserSnippets(settings.userSnippets); 25 25 } 26 + 27 + // update icon 28 + browser.runtime.sendMessage({ updateIcon: true }); 26 29 } 27 30 }, 28 31 });
+4 -2
src/utils/storage.ts
··· 11 11 themes: true, 12 12 snippets: true, 13 13 14 + updated: false, 15 + motd: "Free and <a href='https://github.com/schooltape/schooltape' class='text-(--ctp-accent)'> open source</a>!", 16 + userSnippets: {}, 17 + 14 18 themeFlavour: "mocha", 15 19 themeAccent: "mauve", 16 20 themeLogo: "schooltape-rainbow", 17 21 themeLogoAsFavicon: false, 18 - 19 - userSnippets: {}, 20 22 }, 21 23 }), 22 24 );
+4 -2
src/utils/types.ts
··· 5 5 themes: boolean; 6 6 snippets: boolean; 7 7 8 + updated: boolean; // whether schooltape was recently updated, displays a badge on the icon and renders an info box 9 + motd: string; // message of the day 10 + userSnippets: Record<string, UserSnippet>; 11 + 8 12 themeFlavour: string; 9 13 themeAccent: string; 10 14 themeLogo: LogoId; 11 15 themeLogoAsFavicon: boolean; 12 - 13 - userSnippets: Record<string, UserSnippet>; 14 16 } 15 17 16 18 export type LogoId = "default" | "catppuccin" | "schoolbox" | "schooltape" | "schooltape-rainbow" | "schooltape-legacy";