Add context management

This commit is contained in:
Idrees Hassan
2025-11-02 14:16:24 -05:00
parent 32a871b773
commit 18749acff6
5 changed files with 367 additions and 142 deletions

168
dist/birb.js vendored
View File

@@ -839,6 +839,123 @@
} }
} }
/**
* @typedef {import('./application.js').BirbSaveData} BirbSaveData
*/
/**
* @abstract
*/
class Context {
/**
* @abstract
* @returns {boolean} Whether this context is applicable
*/
isContextActive() {
throw new Error("Method not implemented");
}
/**
* @abstract
* @returns {BirbSaveData|{}}
*/
getSaveData() {
throw new Error("Method not implemented");
}
/**
* @abstract
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
throw new Error("Method not implemented");
}
/**
* @abstract
*/
resetSaveData() {
throw new Error("Method not implemented");
}
}
class LocalContext extends Context {
isContextActive() {
return window.location.hostname === "127.0.0.1"
|| window.location.hostname === "localhost"
|| window.location.hostname.startsWith("192.168.");
}
getSaveData() {
log("Loading save data from localStorage");
return JSON.parse(localStorage.getItem("birbSaveData") ?? "{}");
}
/**
* @override
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
log("Saving data to localStorage");
localStorage.setItem("birbSaveData", JSON.stringify(saveData));
}
resetSaveData() {
log("Resetting save data in localStorage");
localStorage.removeItem("birbSaveData");
}
}
class UserScriptContext extends Context {
isContextActive() {
// @ts-expect-error
return typeof GM_getValue === "function";
}
getSaveData() {
log("Loading save data from UserScript storage");
/** @type {BirbSaveData|{}} */
let saveData = {};
// @ts-expect-error
saveData = GM_getValue("birbSaveData", {}) ?? {};
return saveData;
}
/**
* @override
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
log("Saving data to UserScript storage");
// @ts-expect-error
GM_setValue("birbSaveData", saveData);
}
resetSaveData() {
log("Resetting save data in UserScript storage");
// @ts-expect-error
GM_deleteValue("birbSaveData");
}
}
const CONTEXTS = [
new LocalContext(),
new UserScriptContext(),
];
function getContext() {
for (const context of CONTEXTS) {
if (context.isContextActive()) {
return context;
}
}
error("No applicable context found, defaulting to LocalContext");
return CONTEXTS[0];
}
/** /**
* @typedef {Object} SavedStickyNote * @typedef {Object} SavedStickyNote
* @property {string} id * @property {string} id
@@ -1678,7 +1795,7 @@
insertModal(`${birdBirb()} Mode`, message); insertModal(`${birdBirb()} Mode`, message);
}), }),
new Separator(), new Separator(),
new MenuItem("2025.10.29.20", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.29.20"); }, false), new MenuItem("2025.11.2.0", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.11.2.0"); }, false),
]; ];
const styleElement = document.createElement("style"); const styleElement = document.createElement("style");
@@ -1718,34 +1835,9 @@
/** @type {StickyNote[]} */ /** @type {StickyNote[]} */
let stickyNotes = []; let stickyNotes = [];
/**
* @returns {boolean} Whether the script is running in a userscript extension context
*/
function isUserScript() {
// @ts-expect-error
return typeof GM_getValue === "function";
}
function isTestEnvironment() {
return window.location.hostname === "127.0.0.1"
|| window.location.hostname === "localhost"
|| window.location.hostname.startsWith("192.168.");
}
function load() { function load() {
/** @type {Record<string, any>} */ /** @type {Record<string, any>} */
let saveData = {}; let saveData = getContext().getSaveData();
if (isUserScript()) {
log("Loading save data from UserScript storage");
// @ts-expect-error
saveData = GM_getValue("birbSaveData", {}) ?? {};
} else if (isTestEnvironment()) {
log("Test environment detected, loading save data from localStorage");
saveData = JSON.parse(localStorage.getItem("birbSaveData") ?? "{}");
} else {
log("Not a UserScript");
}
debug("Loaded data: " + JSON.stringify(saveData)); debug("Loaded data: " + JSON.stringify(saveData));
@@ -1788,29 +1880,11 @@
})); }));
} }
if (isUserScript()) { getContext().putSaveData(saveData);
log("Saving data to UserScript storage");
// @ts-expect-error
GM_setValue("birbSaveData", saveData);
} else if (isTestEnvironment()) {
log("Test environment detected, saving data to localStorage");
localStorage.setItem("birbSaveData", JSON.stringify(saveData));
} else {
log("Not a UserScript");
}
} }
function resetSaveData() { function resetSaveData() {
if (isUserScript()) { getContext().resetSaveData();
log("Resetting save data in UserScript storage");
// @ts-expect-error
GM_deleteValue("birbSaveData");
} else if (isTestEnvironment()) {
log("Test environment detected, resetting save data in localStorage");
localStorage.removeItem("birbSaveData");
} else {
log("Not a UserScript");
}
load(); load();
} }

170
dist/birb.user.js vendored
View File

@@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name Pocket Bird // @name Pocket Bird
// @namespace https://idreesinc.com // @namespace https://idreesinc.com
// @version 2025.10.29.20 // @version 2025.11.2.0
// @description birb // @description birb
// @author Idrees // @author Idrees
// @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js // @downloadURL https://github.com/IdreesInc/Pocket-Bird/raw/refs/heads/main/dist/birb.user.js
@@ -853,6 +853,123 @@
} }
} }
/**
* @typedef {import('./application.js').BirbSaveData} BirbSaveData
*/
/**
* @abstract
*/
class Context {
/**
* @abstract
* @returns {boolean} Whether this context is applicable
*/
isContextActive() {
throw new Error("Method not implemented");
}
/**
* @abstract
* @returns {BirbSaveData|{}}
*/
getSaveData() {
throw new Error("Method not implemented");
}
/**
* @abstract
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
throw new Error("Method not implemented");
}
/**
* @abstract
*/
resetSaveData() {
throw new Error("Method not implemented");
}
}
class LocalContext extends Context {
isContextActive() {
return window.location.hostname === "127.0.0.1"
|| window.location.hostname === "localhost"
|| window.location.hostname.startsWith("192.168.");
}
getSaveData() {
log("Loading save data from localStorage");
return JSON.parse(localStorage.getItem("birbSaveData") ?? "{}");
}
/**
* @override
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
log("Saving data to localStorage");
localStorage.setItem("birbSaveData", JSON.stringify(saveData));
}
resetSaveData() {
log("Resetting save data in localStorage");
localStorage.removeItem("birbSaveData");
}
}
class UserScriptContext extends Context {
isContextActive() {
// @ts-expect-error
return typeof GM_getValue === "function";
}
getSaveData() {
log("Loading save data from UserScript storage");
/** @type {BirbSaveData|{}} */
let saveData = {};
// @ts-expect-error
saveData = GM_getValue("birbSaveData", {}) ?? {};
return saveData;
}
/**
* @override
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
log("Saving data to UserScript storage");
// @ts-expect-error
GM_setValue("birbSaveData", saveData);
}
resetSaveData() {
log("Resetting save data in UserScript storage");
// @ts-expect-error
GM_deleteValue("birbSaveData");
}
}
const CONTEXTS = [
new LocalContext(),
new UserScriptContext(),
];
function getContext() {
for (const context of CONTEXTS) {
if (context.isContextActive()) {
return context;
}
}
error("No applicable context found, defaulting to LocalContext");
return CONTEXTS[0];
}
/** /**
* @typedef {Object} SavedStickyNote * @typedef {Object} SavedStickyNote
* @property {string} id * @property {string} id
@@ -1692,7 +1809,7 @@
insertModal(`${birdBirb()} Mode`, message); insertModal(`${birdBirb()} Mode`, message);
}), }),
new Separator(), new Separator(),
new MenuItem("2025.10.29.20", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.10.29.20"); }, false), new MenuItem("2025.11.2.0", () => { alert("Thank you for using Pocket Bird! You are on version: 2025.11.2.0"); }, false),
]; ];
const styleElement = document.createElement("style"); const styleElement = document.createElement("style");
@@ -1732,34 +1849,9 @@
/** @type {StickyNote[]} */ /** @type {StickyNote[]} */
let stickyNotes = []; let stickyNotes = [];
/**
* @returns {boolean} Whether the script is running in a userscript extension context
*/
function isUserScript() {
// @ts-expect-error
return typeof GM_getValue === "function";
}
function isTestEnvironment() {
return window.location.hostname === "127.0.0.1"
|| window.location.hostname === "localhost"
|| window.location.hostname.startsWith("192.168.");
}
function load() { function load() {
/** @type {Record<string, any>} */ /** @type {Record<string, any>} */
let saveData = {}; let saveData = getContext().getSaveData();
if (isUserScript()) {
log("Loading save data from UserScript storage");
// @ts-expect-error
saveData = GM_getValue("birbSaveData", {}) ?? {};
} else if (isTestEnvironment()) {
log("Test environment detected, loading save data from localStorage");
saveData = JSON.parse(localStorage.getItem("birbSaveData") ?? "{}");
} else {
log("Not a UserScript");
}
debug("Loaded data: " + JSON.stringify(saveData)); debug("Loaded data: " + JSON.stringify(saveData));
@@ -1802,29 +1894,11 @@
})); }));
} }
if (isUserScript()) { getContext().putSaveData(saveData);
log("Saving data to UserScript storage");
// @ts-expect-error
GM_setValue("birbSaveData", saveData);
} else if (isTestEnvironment()) {
log("Test environment detected, saving data to localStorage");
localStorage.setItem("birbSaveData", JSON.stringify(saveData));
} else {
log("Not a UserScript");
}
} }
function resetSaveData() { function resetSaveData() {
if (isUserScript()) { getContext().resetSaveData();
log("Resetting save data in UserScript storage");
// @ts-expect-error
GM_deleteValue("birbSaveData");
} else if (isTestEnvironment()) {
log("Test environment detected, resetting save data in localStorage");
localStorage.removeItem("birbSaveData");
} else {
log("Not a UserScript");
}
load(); load();
} }

View File

@@ -2,7 +2,7 @@
"manifest_version": 3, "manifest_version": 3,
"name": "Pocket Bird", "name": "Pocket Bird",
"description": "It's a bird, in your browser. What more could you want?", "description": "It's a bird, in your browser. What more could you want?",
"version": "2025.10.29.20", "version": "2025.11.2.0",
"homepage_url": "https://idreesinc.com", "homepage_url": "https://idreesinc.com",
"icons": { "icons": {
"48": "images/icons/transparent/48x48x1.png", "48": "images/icons/transparent/48x48x1.png",

View File

@@ -2,6 +2,7 @@ import Frame from './frame.js';
import Layer from './layer.js'; import Layer from './layer.js';
import Anim from './anim.js'; import Anim from './anim.js';
import { Birb, Animations } from './birb.js'; import { Birb, Animations } from './birb.js';
import { getContext } from './context.js';
import { import {
Directions, Directions,
@@ -270,34 +271,9 @@ Promise.all([
/** @type {StickyNote[]} */ /** @type {StickyNote[]} */
let stickyNotes = []; let stickyNotes = [];
/**
* @returns {boolean} Whether the script is running in a userscript extension context
*/
function isUserScript() {
// @ts-expect-error
return typeof GM_getValue === "function";
}
function isTestEnvironment() {
return window.location.hostname === "127.0.0.1"
|| window.location.hostname === "localhost"
|| window.location.hostname.startsWith("192.168.");
}
function load() { function load() {
/** @type {Record<string, any>} */ /** @type {Record<string, any>} */
let saveData = {}; let saveData = getContext().getSaveData();
if (isUserScript()) {
log("Loading save data from UserScript storage");
// @ts-expect-error
saveData = GM_getValue("birbSaveData", {}) ?? {};
} else if (isTestEnvironment()) {
log("Test environment detected, loading save data from localStorage");
saveData = JSON.parse(localStorage.getItem("birbSaveData") ?? "{}");
} else {
log("Not a UserScript");
}
debug("Loaded data: " + JSON.stringify(saveData)); debug("Loaded data: " + JSON.stringify(saveData));
@@ -340,29 +316,11 @@ Promise.all([
})); }));
} }
if (isUserScript()) { getContext().putSaveData(saveData);
log("Saving data to UserScript storage");
// @ts-expect-error
GM_setValue("birbSaveData", saveData);
} else if (isTestEnvironment()) {
log("Test environment detected, saving data to localStorage");
localStorage.setItem("birbSaveData", JSON.stringify(saveData));
} else {
log("Not a UserScript");
}
} }
function resetSaveData() { function resetSaveData() {
if (isUserScript()) { getContext().resetSaveData();
log("Resetting save data in UserScript storage");
// @ts-expect-error
GM_deleteValue("birbSaveData");
} else if (isTestEnvironment()) {
log("Test environment detected, resetting save data in localStorage");
localStorage.removeItem("birbSaveData");
} else {
log("Not a UserScript");
}
load(); load();
} }

119
src/context.js Normal file
View File

@@ -0,0 +1,119 @@
import { debug, log, error } from "./shared.js";
/**
* @typedef {import('./application.js').BirbSaveData} BirbSaveData
*/
/**
* @abstract
*/
export class Context {
/**
* @abstract
* @returns {boolean} Whether this context is applicable
*/
isContextActive() {
throw new Error("Method not implemented");
}
/**
* @abstract
* @returns {BirbSaveData|{}}
*/
getSaveData() {
throw new Error("Method not implemented");
}
/**
* @abstract
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
throw new Error("Method not implemented");
}
/**
* @abstract
*/
resetSaveData() {
throw new Error("Method not implemented");
}
}
export class LocalContext extends Context {
isContextActive() {
return window.location.hostname === "127.0.0.1"
|| window.location.hostname === "localhost"
|| window.location.hostname.startsWith("192.168.");
}
getSaveData() {
log("Loading save data from localStorage");
return JSON.parse(localStorage.getItem("birbSaveData") ?? "{}");
}
/**
* @override
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
log("Saving data to localStorage");
localStorage.setItem("birbSaveData", JSON.stringify(saveData));
}
resetSaveData() {
log("Resetting save data in localStorage");
localStorage.removeItem("birbSaveData");
}
}
export class UserScriptContext extends Context {
isContextActive() {
// @ts-expect-error
return typeof GM_getValue === "function";
}
getSaveData() {
log("Loading save data from UserScript storage");
/** @type {BirbSaveData|{}} */
let saveData = {};
// @ts-expect-error
saveData = GM_getValue("birbSaveData", {}) ?? {};
return saveData;
}
/**
* @override
* @param {BirbSaveData} saveData
*/
putSaveData(saveData) {
log("Saving data to UserScript storage");
// @ts-expect-error
GM_setValue("birbSaveData", saveData);
}
resetSaveData() {
log("Resetting save data in UserScript storage");
// @ts-expect-error
GM_deleteValue("birbSaveData");
}
}
const CONTEXTS = [
new LocalContext(),
new UserScriptContext(),
];
export function getContext() {
for (const context of CONTEXTS) {
if (context.isContextActive()) {
return context;
}
}
error("No applicable context found, defaulting to LocalContext");
return CONTEXTS[0];
}