schoolbox web extension :)
1import type { Browser } from "#imports";
2import { browser, defineBackground, storage } from "#imports";
3import { logger } from "@/utils/logger";
4import type { BackgroundMessage } from "@/utils/storage";
5import { globalSettings, updated } from "@/utils/storage";
6import semver from "semver";
7
8export default defineBackground(() => {
9 browser.runtime.onInstalled.addListener(async ({ reason, previousVersion }) => {
10 if (reason === "install") {
11 logger.info("[background] Opening wiki page after install");
12 browser.tabs.create({ url: "https://schooltape.github.io/installed" });
13
14 if (import.meta.env.DEV) {
15 logger.info("[background] Opening development URLs");
16 browser.tabs.create({ url: "https://help.schoolbox.com.au/account/anonymous.php?" });
17 browser.tabs.create({ url: browser.runtime.getURL("/popup.html") });
18 const schoolbox = {
19 url: import.meta.env.WXT_SCHOOLBOX_URL,
20 jwt: import.meta.env.WXT_SCHOOLBOX_JWT,
21 };
22 if (schoolbox.url && schoolbox.jwt) {
23 browser.tabs.create({ url: `${schoolbox.url}/api/session?jwt=${schoolbox.jwt}` });
24 }
25 }
26 } else if (reason === "update") {
27 logger.info("[background] Showing update badge");
28
29 await updated.set({ icon: true, changelog: true });
30 updateIcon();
31
32 const manifest = browser.runtime.getManifest();
33 const newVersion = manifest.version_name || manifest.version;
34
35 // hacky way of resetting the extension to fix migration issues
36 // new version is greater than or equal to v4.0.5 AND previous version was less than v4.0.5
37 if (previousVersion && semver.gte(newVersion, "4.0.5") && semver.lt(previousVersion, "4.0.5")) {
38 logger.info("[background] Clearing storage (v4.0.5 migration)");
39 await storage.clear("local");
40 }
41
42 if (import.meta.env.DEV) {
43 logger.info("[background] Opening development URLs");
44 browser.tabs.create({ url: browser.runtime.getURL("/popup.html"), active: false });
45 }
46 }
47 });
48
49 // update icon when toggle or update is changed
50 globalSettings.watch(updateIcon);
51
52 browser.runtime.onMessage.addListener(async (msg: BackgroundMessage, sender: Browser.runtime.MessageSender) => {
53 logger.info("[background] received message", { message: msg, sender });
54
55 switch (msg.type) {
56 case "resetSettings":
57 resetSettings();
58 break;
59 case "updateIcon":
60 updateIcon();
61 break;
62 case "closeTab":
63 if (!sender.tab?.id) break;
64 browser.tabs.remove(sender.tab.id);
65 break;
66 case "updateTabUrl":
67 if (!sender.tab?.id) break;
68 browser.tabs.update(sender.tab.id, { url: msg.url });
69 break;
70 default:
71 logger.error(`[background] unknown message received: ${msg}`);
72 }
73
74 return true; // return success
75 });
76
77 // context menus
78 let contexts: Browser.contextMenus.CreateProperties["contexts"];
79 logger.info(`[background] Manifest version: ${import.meta.env.MANIFEST_VERSION}`);
80 if (import.meta.env.MANIFEST_VERSION === 2) {
81 contexts = ["browser_action"];
82 } else {
83 contexts = ["action"];
84 }
85 browser.contextMenus.create({
86 id: "report-bug",
87 title: "Report a bug...",
88 contexts: contexts,
89 });
90 browser.contextMenus.create({
91 id: "feature-request",
92 title: "Request a feature...",
93 contexts: contexts,
94 });
95 browser.contextMenus.create({
96 id: "github",
97 title: "GitHub",
98 contexts: contexts,
99 });
100 browser.contextMenus.onClicked.addListener((info) => {
101 const manifest = browser.runtime.getManifest();
102 const version = manifest.version_name || manifest.version;
103
104 switch (info.menuItemId) {
105 case "report-bug":
106 browser.tabs.create({
107 url: `https://github.com/schooltape/schooltape/issues/new?template=bug.yml&version=v${version}`,
108 });
109 break;
110 case "feature-request":
111 browser.tabs.create({
112 url: "https://github.com/schooltape/schooltape/issues/new?template=feature.yml",
113 });
114 break;
115 case "github":
116 browser.tabs.create({ url: "https://github.com/schooltape/schooltape" });
117 break;
118 }
119 });
120});
121
122async function resetSettings(): Promise<void> {
123 logger.info("[background] Clearing local storage");
124 await storage.clear("local");
125}
126
127async function updateIcon() {
128 logger.info("[background] Updating icon...");
129
130 let iconSuffix = "";
131
132 // if it's june
133 if (new Date().getMonth() === 5) {
134 iconSuffix += "-ctp";
135 }
136 if ((await globalSettings.get()).global === false) {
137 iconSuffix += "-disabled";
138 }
139 if ((await updated.get()).icon === true) {
140 iconSuffix += "-badge";
141 }
142
143 if (import.meta.env.MANIFEST_VERSION === 2) {
144 browser.browserAction.setIcon({
145 path: {
146 16: `/icon/16${iconSuffix}.png`,
147 32: `/icon/32${iconSuffix}.png`,
148 48: `/icon/48${iconSuffix}.png`,
149 128: `/icon/128${iconSuffix}.png`,
150 },
151 });
152 } else {
153 browser.action.setIcon({
154 path: {
155 16: `/icon/16${iconSuffix}.png`,
156 32: `/icon/32${iconSuffix}.png`,
157 48: `/icon/48${iconSuffix}.png`,
158 128: `/icon/128${iconSuffix}.png`,
159 },
160 });
161 }
162}