schoolbox web extension :)
0
fork

Configure Feed

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

refactor(storage): config storage for plugins and snippets

willow 995b8411 da34b498

+209 -199
+2 -2
src/entrypoints/plugins/homepageSwitcher.ts
··· 1 1 export default function init() { 2 2 definePlugin( 3 3 "homepageSwitcher", 4 - (_id, data) => { 4 + (_id, data, settings) => { 5 5 const logos = Array.from(document.getElementsByClassName("logo")) as HTMLAnchorElement[]; 6 6 logos.forEach((logo) => { 7 7 logo.addEventListener("click", async function (e) { 8 8 if (window.location.pathname === "/") return; 9 9 e.preventDefault(); 10 10 const tab = logos[0].href; 11 - const closeCurrentTab = await data.settings?.toggle?.closeCurrentTab?.toggle?.storage?.getValue(); 11 + const closeCurrentTab = await settings?.closeCurrentTab.state.storage.getValue(); 12 12 if (closeCurrentTab?.toggle === true) { 13 13 window.close(); 14 14 }
+2 -2
src/utils/plugin.ts
··· 1 1 export async function definePlugin( 2 2 pluginId: PluginId, 3 - injectLogic: (id: PluginId, data: PluginData) => void, 3 + injectLogic: (id: PluginId, data: PluginData, settings?: Record<string, PluginSetting>) => void, 4 4 elementsToWaitFor: string[] = [], 5 5 ) { 6 6 const plugin = await plugins[pluginId].toggle.storage.getValue(); ··· 20 20 if (allElementsPresent) { 21 21 observer.disconnect(); 22 22 logger.info(`all elements present, injecting plugin: ${plugins[pluginId].info.name}`); 23 - injectLogic(pluginId, plugins[pluginId]); 23 + injectLogic(pluginId, plugins[pluginId], plugins[pluginId]?.settings); 24 24 } 25 25 }); 26 26
+124 -139
src/utils/storage/plugins.ts
··· 1 1 import { StorageState } from "./state.svelte"; 2 2 import * as Types from "./types"; 3 3 4 - export const plugins: Record<Types.PluginId, Types.PluginData> = { 5 - subheader: createPlugin( 6 - "subheader", 7 - "Subheader Revamp", 8 - "Adds a clock and current period info to the subheader.", 9 - true, 10 - { 11 - openInNewTab: pluginSetting( 12 - "toggle", 13 - "subheader", 14 - "openInNewTab", 15 - "Open links in new tab", 16 - "Whether to open the class link in a new tab.", 17 - { toggle: true }, 18 - ), 4 + const pluginConfig: Record<Types.PluginId, Types.PluginConfig> = { 5 + subheader: { 6 + name: "Subheader Revamp", 7 + description: "Adds a clock and current period info to the subheader.", 8 + default: true, 9 + settings: { 10 + openInNewTab: { 11 + type: "toggle", 12 + name: "Open links in new tab", 13 + description: "Whether to open the class link in a new tab.", 14 + default: { toggle: true }, 15 + }, 19 16 }, 20 - ), 21 - 22 - scrollSegments: createPlugin( 23 - "scrollSegments", 24 - "Scroll Segments", 25 - "Segments the Schoolbox page into scrollable sections.", 26 - true, 27 - ), 28 - 29 - scrollPeriod: createPlugin("scrollPeriod", "Scroll Period", "Scrolls to the current period on the timetable.", true, { 30 - resetCooldownOnMouseMove: pluginSetting( 31 - "toggle", 32 - "scrollPeriod", 33 - "resetCooldownOnMouseMove", 34 - "Reset on mouse move", 35 - "Whether to reset the scrolling cooldown when you move your mouse.", 36 - { toggle: true }, 37 - ), 38 - cooldownDuration: pluginSetting( 39 - "slider", 40 - "scrollPeriod", 41 - "cooldownDuration", 42 - "Cooldown duration (s)", 43 - "How long to wait before scrolling.", 44 - { min: 1, max: 60, value: 10 }, 45 - ), 46 - }), 47 - 48 - progressBar: createPlugin( 49 - "progressBar", 50 - "Progress Bar", 51 - "Displays a progress bar below the timetable to show the time of the day.", 52 - true, 53 - ), 54 - 55 - modernIcons: createPlugin("modernIcons", "Modern Icons", "Modernise the icons across Schoolbox.", true, { 56 - filled: pluginSetting( 57 - "toggle", 58 - "modernIcons", 59 - "filled", 60 - "Filled Icons", 61 - "Whether the icons should be filled or outlined.", 62 - { toggle: true }, 63 - ), 64 - }), 65 - 66 - tabTitle: createPlugin("tabTitle", "Better Tab Titles", "Improves the tab titles for easier navigation.", true, { 67 - showSubjectPrefix: pluginSetting( 68 - "toggle", 69 - "tabTitle", 70 - "showSubjectPrefix", 71 - "Show subject prefix", 72 - `e.g. "ENG - VCE English 1 & 2" becomes "VCE English 1 & 2"`, 73 - { toggle: true }, 74 - ), 75 - }), 76 - 77 - homepageSwitcher: createPlugin( 78 - "homepageSwitcher", 79 - "Homepage Switcher", 80 - "The logo will switch to existing Schoolbox homepage when available.", 81 - true, 82 - { 83 - closeCurrentTab: pluginSetting( 84 - "toggle", 85 - "homepageSwitcher", 86 - "closeCurrentTab", 87 - "Close current tab", 88 - "When switching to another tab, close the current one.", 89 - { toggle: true }, 90 - ), 17 + }, 18 + scrollSegments: { 19 + name: "Scroll Segments", 20 + description: "Segments the Schoolbox page into scrollable sections.", 21 + default: true, 22 + }, 23 + scrollPeriod: { 24 + name: "Scroll Period", 25 + description: "Scrolls to the current period on the timetable.", 26 + default: true, 27 + settings: { 28 + resetCooldownOnMouseMove: { 29 + type: "toggle", 30 + name: "Reset on mouse move", 31 + description: "Whether to reset the scrolling cooldown when you move your mouse.", 32 + default: { toggle: true }, 33 + }, 34 + cooldownDuration: { 35 + type: "slider", 36 + name: "Cooldown duration (s)", 37 + description: "How long to wait before scrolling.", 38 + default: { min: 1, max: 60, value: 10 }, 39 + }, 40 + }, 41 + }, 42 + progressBar: { 43 + name: "Progress Bar", 44 + description: "Displays a progress bar below the timetable to show the time of the day.", 45 + default: true, 46 + }, 47 + modernIcons: { 48 + name: "Modern Icons", 49 + description: "Modernise the icons across Schoolbox.", 50 + default: true, 51 + settings: { 52 + filled: { 53 + type: "toggle", 54 + name: "Filled Icons", 55 + description: "Whether the icons should be filled or outlined.", 56 + default: { toggle: true }, 57 + }, 58 + }, 59 + }, 60 + tabTitle: { 61 + name: "Better Tab Titles", 62 + description: "Improves the tab titles for easier navigation.", 63 + default: true, 64 + settings: { 65 + showSubjectPrefix: { 66 + type: "toggle", 67 + name: "Show subject prefix", 68 + description: `e.g. "ENG - VCE English 1 & 2" becomes "VCE English 1 & 2"`, 69 + default: { toggle: true }, 70 + }, 71 + }, 72 + }, 73 + homepageSwitcher: { 74 + name: "Homepage Switcher", 75 + description: "The logo will switch to existing Schoolbox homepage when available.", 76 + default: true, 77 + settings: { 78 + closeCurrentTab: { 79 + type: "toggle", 80 + name: "Close current tab", 81 + description: "When switching to another tab, close the current one.", 82 + default: { toggle: true }, 83 + }, 91 84 }, 92 - ), 85 + }, 93 86 }; 94 87 95 - function createPlugin( 96 - id: string, 97 - name: string, 98 - description: string, 99 - fallbackToggle: boolean, 100 - settings?: Record<string, Types.PluginSetting>, 101 - ) { 102 - const plugin: Types.PluginData = { 103 - toggle: new StorageState( 104 - storage.defineItem<Types.ToggleState>(`local:plugin-${id}`, { 105 - fallback: { 106 - toggle: fallbackToggle, 107 - }, 108 - }), 109 - true, 110 - ), 111 - info: { 112 - name, 113 - description, 114 - }, 115 - }; 88 + export const plugins = buildPluginsFromConfig(pluginConfig); 116 89 117 - if (settings) { 118 - plugin.settings = settings; 119 - } 90 + function buildPluginsFromConfig(config: Record<PluginId, Types.PluginConfig>): Record<Types.PluginId, Types.Plugin> { 91 + const plugins: Partial<Record<Types.PluginId, Types.Plugin>> = {}; 120 92 121 - return plugin; 122 - } 123 - 124 - export function pluginSetting( 125 - type: "toggle" | "slider", 126 - pluginId: Types.PluginId, 127 - settingId: string, 128 - name: string, 129 - description: string, 130 - fallback: Types.ToggleState | Types.SliderState, 131 - ): Types.PluginSetting { 132 - if (type === "toggle") { 133 - return { 134 - type: "toggle", 135 - state: new StorageState( 136 - storage.defineItem<Types.ToggleState>(`local:${pluginId}-${settingId}`, { 137 - fallback: fallback as Types.ToggleState, 93 + for (const [pluginId, pluginConfig] of Object.entries(config)) { 94 + const plugin: Types.Plugin = { 95 + name: pluginConfig.name, 96 + description: pluginConfig.description, 97 + toggle: new StorageState( 98 + storage.defineItem<Types.Toggle>(`local:plugin-${pluginId}`, { 99 + fallback: { toggle: pluginConfig.default }, 138 100 }), 101 + true, 139 102 ), 140 - info: { name, description }, 141 103 }; 142 - } else { 143 - return { 144 - type: "slider", 145 - state: new StorageState( 146 - storage.defineItem<Types.SliderState>(`local:${pluginId}-${settingId}`, { 147 - fallback: fallback as Types.SliderState, 148 - }), 149 - ), 150 - info: { name, description }, 151 - }; 104 + 105 + // define settings 106 + if (pluginConfig.settings) { 107 + plugin.settings = {}; 108 + 109 + // iterate over each setting and create state 110 + for (const [settingId, settingConfig] of Object.entries(pluginConfig.settings)) { 111 + let state; 112 + if (settingConfig.type === "toggle") { 113 + state = new StorageState( 114 + storage.defineItem<Types.Toggle>(`local:plugin-${pluginId}-${settingId}`, { 115 + fallback: settingConfig.default, 116 + }), 117 + ); 118 + } else { 119 + state = new StorageState( 120 + storage.defineItem<Types.Slider>(`local:plugin-${pluginId}-${settingId}`, { 121 + fallback: settingConfig.default, 122 + }), 123 + ); 124 + } 125 + plugin.settings[settingId] = { 126 + state, 127 + type: settingConfig.type, 128 + name: settingConfig.name, 129 + description: settingConfig.description, 130 + } as Types.PluginSetting; 131 + } 132 + } 133 + 134 + plugins[pluginId as Types.PluginId] = plugin; 152 135 } 136 + 137 + return plugins as Record<Types.PluginId, Types.Plugin>; 153 138 }
+44 -35
src/utils/storage/snippets.ts
··· 1 1 import { StorageState } from "./state.svelte"; 2 2 import * as Types from "./types"; 3 3 4 - export const snippets: Record<Types.SnippetId, Types.SnippetData> = { 5 - roundedCorners: createSnippet( 6 - "roundedCorners", 7 - "Rounded Corners", 8 - "Adds rounded corners to all elements across Schoolbox.", 9 - true, 10 - ), 4 + export const snippetConfig: Record<Types.SnippetId, Types.SnippetConfig> = { 5 + roundedCorners: { 6 + name: "Rounded Corners", 7 + description: "Adds rounded corners to all elements across Schoolbox.", 8 + default: true, 9 + }, 11 10 12 - hidePfp: createSnippet("hidePfp", "Hide PFP", "Hide your profile picture across Schoolbox.", true), 11 + hidePfp: { 12 + name: "Hide PFP", 13 + description: "Hide your profile picture across Schoolbox.", 14 + default: true, 15 + }, 13 16 14 - hidePwaPrompt: createSnippet( 15 - "hidePwaPrompt", 16 - "Hide PWA Prompt", 17 - "Hides the prompt in the notifications menu to install Schoolbox as a PWA and enable notifications.", 18 - true, 19 - ), 17 + hidePwaPrompt: { 18 + name: "Hide PWA Prompt", 19 + description: "Hides the prompt in the notifications menu to install Schoolbox as a PWA and enable notifications.", 20 + default: true, 21 + }, 20 22 21 - censor: createSnippet( 22 - "censor", 23 - "Censor", 24 - "Censors all text and images. This is intended for development purposes.", 25 - false, 26 - ), 23 + censor: { 24 + name: "Censor", 25 + description: "Censors all text and images. This is intended for development purposes.", 26 + default: false, 27 + }, 27 28 }; 28 29 29 - function createSnippet(id: string, name: string, description: string, fallbackToggle: boolean) { 30 - return { 31 - toggle: new StorageState( 32 - storage.defineItem<Types.ToggleState>(`local:snippet-${id}`, { 33 - fallback: { 34 - toggle: fallbackToggle, 35 - }, 36 - }), 37 - true, 38 - ), 39 - info: { 40 - name, 41 - description, 42 - }, 43 - }; 30 + export const snippets = buildSnippetsFromConfig(snippetConfig); 31 + 32 + function buildSnippetsFromConfig( 33 + config: Record<SnippetId, Types.SnippetConfig>, 34 + ): Record<Types.SnippetId, Types.Snippet> { 35 + const snippets: Partial<Record<Types.SnippetId, Types.Snippet>> = {}; 36 + 37 + for (const [snippetId, snippetConfig] of Object.entries(config)) { 38 + const snippet: Types.Snippet = { 39 + name: snippetConfig.name, 40 + description: snippetConfig.description, 41 + toggle: new StorageState( 42 + storage.defineItem<Types.Toggle>(`local:snippet-${snippetId}`, { 43 + fallback: { toggle: snippetConfig.default }, 44 + }), 45 + true, 46 + ), 47 + }; 48 + 49 + snippets[snippetId as Types.SnippetId] = snippet; 50 + } 51 + 52 + return snippets as Record<Types.SnippetId, Types.Snippet>; 44 53 }
+37 -21
src/utils/storage/types.ts
··· 61 61 | "tabTitle" 62 62 | "homepageSwitcher"; 63 63 64 - export type ToggleState = { toggle: boolean }; 65 - type Toggle = { 66 - type: "toggle"; 67 - state: StorageState<ToggleState>; 68 - info: ItemInfo; 69 - }; 64 + export type Toggle = { toggle: boolean }; 70 65 71 - export type SliderState = { 66 + export type Slider = { 72 67 value: number; 73 68 min: number; 74 69 max: number; 75 70 }; 76 - type Slider = { 77 - type: "slider"; 78 - state: StorageState<SliderState>; 79 - info: ItemInfo; 80 - }; 81 - export type PluginSetting = Toggle | Slider; 82 71 83 - export type PluginData = { 84 - toggle: StorageState<ToggleState>; 85 - info: ItemInfo; 72 + export type Plugin = { 73 + toggle: StorageState<Toggle>; 86 74 settings?: Record<string, PluginSetting>; 87 - }; 75 + } & ItemInfo; 76 + 77 + export type PluginConfig = { 78 + default: boolean; 79 + settings?: Record<string, PluginSettingConfig>; 80 + } & ItemInfo; 81 + 82 + export type PluginSetting = 83 + | ({ 84 + type: "toggle"; 85 + state: StorageState<Toggle>; 86 + } & ItemInfo) 87 + | ({ 88 + type: "slider"; 89 + state: StorageState<Slider>; 90 + } & ItemInfo); 91 + 92 + export type PluginSettingConfig = 93 + | ({ 94 + type: "toggle"; 95 + default: Toggle; 96 + } & ItemInfo) 97 + | ({ 98 + type: "slider"; 99 + default: Slider; 100 + } & ItemInfo); 88 101 89 102 // Snippets 90 103 export type SnippetId = "roundedCorners" | "hidePfp" | "hidePwaPrompt" | "censor"; 91 104 92 - export type SnippetData = { 93 - toggle: StorageState<ToggleState>; 94 - info: ItemInfo; 95 - }; 105 + export type Snippet = { 106 + toggle: StorageState<Toggle>; 107 + } & ItemInfo; 108 + 109 + export type SnippetConfig = { 110 + default: boolean; 111 + } & ItemInfo;
src/utils/storage/utils.ts

This is a binary file and will not be displayed.