Compare commits
2 Commits
5fb151807e
...
bd0f7a7a8c
Author | SHA1 | Date |
---|---|---|
Bauke | bd0f7a7a8c | |
Bauke | 2e58f23239 |
|
@ -15,6 +15,7 @@
|
||||||
"@bauke/eslint-config": "^0.1.2",
|
"@bauke/eslint-config": "^0.1.2",
|
||||||
"@bauke/prettier-config": "^0.1.2",
|
"@bauke/prettier-config": "^0.1.2",
|
||||||
"@bauke/stylelint-config": "^0.1.2",
|
"@bauke/stylelint-config": "^0.1.2",
|
||||||
|
"@holllo/test": "^0.2.1",
|
||||||
"@types/debounce": "^1.2.1",
|
"@types/debounce": "^1.2.1",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
"@types/platform": "^1.3.4",
|
"@types/platform": "^1.3.4",
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
lockfileVersion: '6.0'
|
lockfileVersion: '6.0'
|
||||||
|
|
||||||
|
settings:
|
||||||
|
autoInstallPeers: true
|
||||||
|
excludeLinksFromLockfile: false
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@holllo/migration-helper':
|
'@holllo/migration-helper':
|
||||||
specifier: ^0.1.4
|
specifier: ^0.1.4
|
||||||
|
@ -36,6 +40,9 @@ devDependencies:
|
||||||
'@bauke/stylelint-config':
|
'@bauke/stylelint-config':
|
||||||
specifier: ^0.1.2
|
specifier: ^0.1.2
|
||||||
version: 0.1.2(stylelint-config-standard-scss@6.1.0)(stylelint@15.9.0)
|
version: 0.1.2(stylelint-config-standard-scss@6.1.0)(stylelint@15.9.0)
|
||||||
|
'@holllo/test':
|
||||||
|
specifier: ^0.2.1
|
||||||
|
version: 0.2.1
|
||||||
'@types/debounce':
|
'@types/debounce':
|
||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
|
@ -713,6 +720,10 @@ packages:
|
||||||
resolution: {integrity: sha512-4XMYlIOSFzTDWSFizKRSYUHWEfQBNZbPI/Tc5Qhef4lhxZBCAzJt1+RutB5TpCdR6sllJN+Dt5WblCmZXCaoLw==}
|
resolution: {integrity: sha512-4XMYlIOSFzTDWSFizKRSYUHWEfQBNZbPI/Tc5Qhef4lhxZBCAzJt1+RutB5TpCdR6sllJN+Dt5WblCmZXCaoLw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@holllo/test@0.2.1:
|
||||||
|
resolution: {integrity: sha512-QlIvEqvuEfu8vapnwai8A+1TmZGkPObgU32VEXHBc3XEKhupHZRFB778oLPYlJVuSsi4TT99890iSR3nlvVwtQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@holllo/webextension-storage@0.2.0(webextension-polyfill@0.10.0):
|
/@holllo/webextension-storage@0.2.0(webextension-polyfill@0.10.0):
|
||||||
resolution: {integrity: sha512-WiSkkY/Jg3PhlHOH8eGvRBBtvZwHrJ0FD/LF8lNZAc3uaRdonF79o/Xt9CefYUjV6FSbHl/vsccXyAoitvkRIQ==}
|
resolution: {integrity: sha512-WiSkkY/Jg3PhlHOH8eGvRBBtvZwHrJ0FD/LF8lNZAc3uaRdonF79o/Xt9CefYUjV6FSbHl/vsccXyAoitvkRIQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import browser from "webextension-polyfill";
|
import browser from "webextension-polyfill";
|
||||||
|
import {migrations} from "../storage/migrations/migrations.js";
|
||||||
|
import {log} from "../utilities/logging.js";
|
||||||
|
|
||||||
if ($browser === "firefox") {
|
if ($browser === "firefox") {
|
||||||
browser.browserAction.onClicked.addListener(openOptionsPage);
|
browser.browserAction.onClicked.addListener(openOptionsPage);
|
||||||
|
@ -7,6 +9,14 @@ if ($browser === "firefox") {
|
||||||
}
|
}
|
||||||
|
|
||||||
browser.runtime.onInstalled.addListener(async () => {
|
browser.runtime.onInstalled.addListener(async () => {
|
||||||
|
const existingStorage = await browser.storage.sync.get();
|
||||||
|
if (existingStorage.version === "1.1.2") {
|
||||||
|
log("Running 1.1.2 to 2.0.0 data migration.", true);
|
||||||
|
await browser.storage.local.set({backup: JSON.stringify(existingStorage)});
|
||||||
|
await browser.storage.sync.clear();
|
||||||
|
await migrations[0].migrate(existingStorage);
|
||||||
|
}
|
||||||
|
|
||||||
if ($dev) {
|
if ($dev) {
|
||||||
await openOptionsPage();
|
await openOptionsPage();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,11 @@ import {type Feature, Data, fromStorage} from "../storage/common.js";
|
||||||
import {AppContext} from "./context.js";
|
import {AppContext} from "./context.js";
|
||||||
import {features} from "./features.js";
|
import {features} from "./features.js";
|
||||||
|
|
||||||
window.addEventListener("load", async () => {
|
window.addEventListener("DOMContentLoaded", async () => {
|
||||||
|
if ($test) {
|
||||||
|
await import("../storage/migrations/migrations.test.js");
|
||||||
|
}
|
||||||
|
|
||||||
initializeGlobals();
|
initializeGlobals();
|
||||||
const manifest = browser.runtime.getManifest();
|
const manifest = browser.runtime.getManifest();
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import {type Value, createValue} from "@holllo/webextension-storage";
|
import {createValue} from "@holllo/webextension-storage";
|
||||||
import browser from "webextension-polyfill";
|
import browser from "webextension-polyfill";
|
||||||
|
|
||||||
export enum Feature {
|
export enum Feature {
|
||||||
AnonymizeUsernames = "anonymize-users",
|
AnonymizeUsernames = "anonymize-usernames",
|
||||||
Autocomplete = "autocomplete",
|
Autocomplete = "autocomplete",
|
||||||
BackToTop = "back-to-top",
|
BackToTop = "back-to-top",
|
||||||
Debug = "debug",
|
Debug = "debug",
|
||||||
|
@ -18,6 +18,7 @@ export enum Data {
|
||||||
EnabledFeatures = "enabled-features",
|
EnabledFeatures = "enabled-features",
|
||||||
KnownGroups = "known-groups",
|
KnownGroups = "known-groups",
|
||||||
LatestActiveFeatureTab = "latest-active-feature-tab",
|
LatestActiveFeatureTab = "latest-active-feature-tab",
|
||||||
|
Version = "data-version",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HideVotesData = {
|
export type HideVotesData = {
|
||||||
|
@ -111,6 +112,13 @@ export const storageValues = {
|
||||||
value: Feature.Debug,
|
value: Feature.Debug,
|
||||||
storage: browser.storage.sync,
|
storage: browser.storage.sync,
|
||||||
}),
|
}),
|
||||||
|
[Data.Version]: createValue({
|
||||||
|
deserialize: (input) => JSON.parse(input) as string,
|
||||||
|
serialize: (input) => JSON.stringify(input),
|
||||||
|
key: Data.Version,
|
||||||
|
value: "2.0.0",
|
||||||
|
storage: browser.storage.sync,
|
||||||
|
}),
|
||||||
[Feature.HideVotes]: createValue({
|
[Feature.HideVotes]: createValue({
|
||||||
deserialize: (input) => JSON.parse(input) as HideVotesData,
|
deserialize: (input) => JSON.parse(input) as HideVotesData,
|
||||||
serialize: (input) => JSON.stringify(input),
|
serialize: (input) => JSON.stringify(input),
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
import {setup} from "@holllo/test";
|
||||||
|
import {Data, Feature} from "../common.js";
|
||||||
|
import {migrations} from "./migrations.js";
|
||||||
|
import {v112Sample} from "./v1-1-2.js";
|
||||||
|
|
||||||
|
await setup("Migrations", async (group) => {
|
||||||
|
group.test("2.0.0", async (test) => {
|
||||||
|
await browser.storage.sync.clear();
|
||||||
|
|
||||||
|
await migrations[0].migrate(v112Sample);
|
||||||
|
const storage = await browser.storage.sync.get();
|
||||||
|
for (const [key, value] of Object.entries(storage)) {
|
||||||
|
switch (key) {
|
||||||
|
case Data.EnabledFeatures: {
|
||||||
|
test.equals(
|
||||||
|
value,
|
||||||
|
'["autocomplete","back-to-top","debug","hide-votes","jump-to-new-comment","markdown-toolbar","themed-logo","user-labels"]',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Data.KnownGroups: {
|
||||||
|
test.equals(value, '["~group","~group.subgroup","~test"]');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Data.Version: {
|
||||||
|
test.equals(value, '"2.0.0"');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Feature.HideVotes: {
|
||||||
|
test.equals(
|
||||||
|
value,
|
||||||
|
'{"otherComments":true,"otherTopics":true,"ownComments":true,"ownTopics":false}',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Feature.UsernameColors: {
|
||||||
|
test.equals(
|
||||||
|
value,
|
||||||
|
'[{"color":"red","id":4,"username":"Test"},{"color":"green","id":18,"username":"AnotherTest"}]',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case `${Feature.UserLabels}-1`: {
|
||||||
|
test.equals(
|
||||||
|
value,
|
||||||
|
'{"color":"#ff00ff","id":1,"priority":0,"text":"Test Label","username":"Test"}',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case `${Feature.UserLabels}-15`: {
|
||||||
|
test.equals(
|
||||||
|
value,
|
||||||
|
'{"id":15,"color":"var(--syntax-string-color)","priority":0,"text":"Another Label","username":"AnotherTest"}',
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
console.log(key, JSON.stringify(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,52 @@
|
||||||
|
import {setup} from "@holllo/test";
|
||||||
|
import {type Migration} from "@holllo/migration-helper";
|
||||||
|
import browser from "webextension-polyfill";
|
||||||
|
import {Data, Feature, fromStorage, saveUserLabels} from "../common.js";
|
||||||
|
import {v112DeserializeData, v112Sample, type V112Settings} from "./v1-1-2.js";
|
||||||
|
|
||||||
|
export const migrations: Array<Migration<string>> = [
|
||||||
|
{
|
||||||
|
version: "2.0.0",
|
||||||
|
async migrate(data: V112Settings): Promise<void> {
|
||||||
|
const deserialized = v112DeserializeData(data);
|
||||||
|
data.data.userLabels = deserialized.userLabels;
|
||||||
|
data.data.usernameColors = deserialized.usernameColors;
|
||||||
|
await saveUserLabels(data.data.userLabels);
|
||||||
|
|
||||||
|
const hideVotes = await fromStorage(Feature.HideVotes);
|
||||||
|
hideVotes.value = {
|
||||||
|
otherComments: data.data.hideVotes.comments,
|
||||||
|
otherTopics: data.data.hideVotes.topics,
|
||||||
|
ownComments: data.data.hideVotes.ownComments,
|
||||||
|
ownTopics: data.data.hideVotes.ownTopics,
|
||||||
|
};
|
||||||
|
await hideVotes.save();
|
||||||
|
|
||||||
|
const knownGroups = await fromStorage(Data.KnownGroups);
|
||||||
|
knownGroups.value = new Set(data.data.knownGroups);
|
||||||
|
await knownGroups.save();
|
||||||
|
|
||||||
|
const version = await fromStorage(Data.Version);
|
||||||
|
version.value = "2.0.0";
|
||||||
|
await version.save();
|
||||||
|
|
||||||
|
const usernameColors = await fromStorage(Feature.UsernameColors);
|
||||||
|
usernameColors.value = data.data.usernameColors;
|
||||||
|
await usernameColors.save();
|
||||||
|
|
||||||
|
const enabledFeatures = await fromStorage(Data.EnabledFeatures);
|
||||||
|
for (const [key, value] of Object.entries(data.features)) {
|
||||||
|
if (value) {
|
||||||
|
const snakeCasedKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
||||||
|
if (Object.values(Feature).includes(snakeCasedKey as Feature)) {
|
||||||
|
enabledFeatures.value.add(snakeCasedKey as Feature);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown key: ${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await enabledFeatures.save();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,114 @@
|
||||||
|
export function v112DeserializeData(data: Record<string, any>): {
|
||||||
|
userLabels: V112Settings["data"]["userLabels"];
|
||||||
|
usernameColors: V112Settings["data"]["usernameColors"];
|
||||||
|
} {
|
||||||
|
const deserialized: ReturnType<typeof v112DeserializeData> = {
|
||||||
|
userLabels: [],
|
||||||
|
usernameColors: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(data)) {
|
||||||
|
if (key.startsWith("userLabel")) {
|
||||||
|
deserialized.userLabels.push(
|
||||||
|
value as (typeof deserialized)["userLabels"][number],
|
||||||
|
);
|
||||||
|
} else if (key.startsWith("usernameColor")) {
|
||||||
|
deserialized.usernameColors.push(
|
||||||
|
value as (typeof deserialized)["usernameColors"][number],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deserialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type V112Settings = {
|
||||||
|
[index: string]: any;
|
||||||
|
data: {
|
||||||
|
hideVotes: {
|
||||||
|
comments: boolean;
|
||||||
|
topics: boolean;
|
||||||
|
ownComments: boolean;
|
||||||
|
ownTopics: boolean;
|
||||||
|
};
|
||||||
|
knownGroups: string[];
|
||||||
|
latestActiveFeatureTab: string;
|
||||||
|
userLabels: Array<{
|
||||||
|
color: string;
|
||||||
|
id: number;
|
||||||
|
priority: number;
|
||||||
|
text: string;
|
||||||
|
username: string;
|
||||||
|
}>;
|
||||||
|
usernameColors: Array<{
|
||||||
|
color: string;
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
features: {
|
||||||
|
anonymizeUsernames: boolean;
|
||||||
|
autocomplete: boolean;
|
||||||
|
backToTop: boolean;
|
||||||
|
debug: boolean;
|
||||||
|
hideVotes: boolean;
|
||||||
|
jumpToNewComment: boolean;
|
||||||
|
markdownToolbar: boolean;
|
||||||
|
themedLogo: boolean;
|
||||||
|
userLabels: boolean;
|
||||||
|
usernameColors: boolean;
|
||||||
|
};
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const v112Sample: V112Settings = {
|
||||||
|
data: {
|
||||||
|
hideVotes: {
|
||||||
|
comments: true,
|
||||||
|
ownComments: true,
|
||||||
|
ownTopics: false,
|
||||||
|
topics: true,
|
||||||
|
},
|
||||||
|
knownGroups: ["~group", "~group.subgroup", "~test"],
|
||||||
|
latestActiveFeatureTab: "userLabels",
|
||||||
|
userLabels: [],
|
||||||
|
usernameColors: [],
|
||||||
|
},
|
||||||
|
features: {
|
||||||
|
anonymizeUsernames: false,
|
||||||
|
autocomplete: true,
|
||||||
|
backToTop: true,
|
||||||
|
debug: true,
|
||||||
|
hideVotes: true,
|
||||||
|
jumpToNewComment: true,
|
||||||
|
markdownToolbar: true,
|
||||||
|
themedLogo: true,
|
||||||
|
userLabels: true,
|
||||||
|
usernameColors: false,
|
||||||
|
},
|
||||||
|
version: "1.1.2",
|
||||||
|
userLabel1: {
|
||||||
|
color: "#ff00ff",
|
||||||
|
id: 1,
|
||||||
|
priority: 0,
|
||||||
|
text: "Test Label",
|
||||||
|
username: "Test",
|
||||||
|
},
|
||||||
|
userLabel15: {
|
||||||
|
id: 15,
|
||||||
|
color: "var(--syntax-string-color)",
|
||||||
|
priority: 0,
|
||||||
|
text: "Another Label",
|
||||||
|
username: "AnotherTest",
|
||||||
|
},
|
||||||
|
usernameColor4: {
|
||||||
|
color: "red",
|
||||||
|
id: 4,
|
||||||
|
username: "Test",
|
||||||
|
},
|
||||||
|
usernameColor18: {
|
||||||
|
color: "green",
|
||||||
|
id: 18,
|
||||||
|
username: "AnotherTest",
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,3 +1,5 @@
|
||||||
|
import {type JSX} from "preact";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
class?: string;
|
class?: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
@ -5,7 +7,7 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/** An `<a />` helper component with `target="_blank"` and `rel="noopener"`. */
|
/** An `<a />` helper component with `target="_blank"` and `rel="noopener"`. */
|
||||||
export function Link(props: Props): TRXComponent {
|
export function Link(props: Props): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<a class={props.class} href={props.url} target="_blank" rel="noopener">
|
<a class={props.class} href={props.url} target="_blank" rel="noopener">
|
||||||
{props.text}
|
{props.text}
|
||||||
|
|
Loading…
Reference in New Issue