Rework the Item code so it can be reused for the history.
This commit is contained in:
parent
451ebb8189
commit
091da70bea
|
@ -1,8 +1,13 @@
|
||||||
import {type TestContext, setup} from "@holllo/test";
|
import {type TestContext, setup} from "@holllo/test";
|
||||||
import {type Value} from "@holllo/webextension-storage";
|
import {type Value} from "@holllo/webextension-storage";
|
||||||
import browser from "webextension-polyfill";
|
|
||||||
|
|
||||||
import {type Item, createItem, nextItem, nextItemId, storage} from "./item.js";
|
import {
|
||||||
|
type Item,
|
||||||
|
createItem,
|
||||||
|
nextItem,
|
||||||
|
nextItemId,
|
||||||
|
storageForPrefix,
|
||||||
|
} from "./item.js";
|
||||||
|
|
||||||
const testText = "Test Item";
|
const testText = "Test Item";
|
||||||
const testUrl = "https://example.org/";
|
const testUrl = "https://example.org/";
|
||||||
|
@ -30,10 +35,8 @@ await setup(
|
||||||
"Item",
|
"Item",
|
||||||
async (group) => {
|
async (group) => {
|
||||||
group.beforeAll(async () => {
|
group.beforeAll(async () => {
|
||||||
// If we're in production and testing, clear item storage.
|
// TODO: Temporarily store existing storage and run the tests, and then
|
||||||
if (!$dev && $test) {
|
// restore it again.
|
||||||
await storage.clear();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
group.test("create & nextItem", async (test) => {
|
group.test("create & nextItem", async (test) => {
|
||||||
|
@ -67,11 +70,7 @@ await setup(
|
||||||
await Promise.all(items.map(async (item) => item.remove()));
|
await Promise.all(items.map(async (item) => item.remove()));
|
||||||
|
|
||||||
// After all items have been removed test that `nextItem` returns nothing.
|
// After all items have been removed test that `nextItem` returns nothing.
|
||||||
// This test will fail if an item is left from development, to clear
|
// This test will fail if an item is left from development.
|
||||||
// storage automatically run in production and have test enabled.
|
|
||||||
// ie. `NODE_ENV=production TEST=true makers dev`
|
|
||||||
// TODO: Temporarily store existing storage and run the tests, and then
|
|
||||||
// restore it again.
|
|
||||||
storedNext = await nextItem();
|
storedNext = await nextItem();
|
||||||
test.equals(storedNext, undefined, "next item is undefined");
|
test.equals(storedNext, undefined, "next item is undefined");
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,10 +29,13 @@ export type SerializedItem = {
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The key prefix for {@link Item}s. */
|
/** The key prefix for {@link Item}s. */
|
||||||
export const itemKeyPrefix = "item-";
|
export type ItemKeyPrefix = "item-" | "history-";
|
||||||
|
|
||||||
/** The default storage area to use for {@link Item}s. */
|
export function storageForPrefix(
|
||||||
export const storage = browser.storage.sync;
|
prefix: ItemKeyPrefix,
|
||||||
|
): browser.Storage.StorageArea {
|
||||||
|
return prefix === "item-" ? browser.storage.sync : browser.storage.local;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize and JSON-stringify an {@link Item}.
|
* Serialize and JSON-stringify an {@link Item}.
|
||||||
|
@ -83,18 +86,20 @@ export const deserializeItem: Value<Item>["deserialize"] = (
|
||||||
*
|
*
|
||||||
* @param text The text of the {@link Item} to create.
|
* @param text The text of the {@link Item} to create.
|
||||||
* @param url The URL of the {@link Item} to create.
|
* @param url The URL of the {@link Item} to create.
|
||||||
|
* @param itemKeyPrefix The prefix for the {@link Item} key.
|
||||||
* @returns The created {@link Value} with inner {@link Item}.
|
* @returns The created {@link Value} with inner {@link Item}.
|
||||||
*/
|
*/
|
||||||
export async function createItem(
|
export async function createItem(
|
||||||
text: Item["text"],
|
text: Item["text"],
|
||||||
url: Item["url"],
|
url: Item["url"],
|
||||||
|
itemKeyPrefix: ItemKeyPrefix = "item-",
|
||||||
): Promise<Value<Item>> {
|
): Promise<Value<Item>> {
|
||||||
const nextId = await nextItemId();
|
const nextId = await nextItemId();
|
||||||
|
|
||||||
const item = await createValue<Item>({
|
const item = await createValue<Item>({
|
||||||
deserialize: deserializeItem,
|
deserialize: deserializeItem,
|
||||||
serialize: serializeItem,
|
serialize: serializeItem,
|
||||||
storage,
|
storage: storageForPrefix(itemKeyPrefix),
|
||||||
key: `${itemKeyPrefix}${nextId}`,
|
key: `${itemKeyPrefix}${nextId}`,
|
||||||
value: {
|
value: {
|
||||||
dateAdded: new Date(),
|
dateAdded: new Date(),
|
||||||
|
@ -108,11 +113,14 @@ export async function createItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all keys from storage that start with the {@link itemKeyPrefix}.
|
* Get all keys from storage that start with the {@link ItemKeyPrefix}.
|
||||||
*
|
*
|
||||||
* @returns The keys as a string array.
|
* @returns The keys as a string array.
|
||||||
*/
|
*/
|
||||||
export async function getItemKeys(): Promise<string[]> {
|
export async function getItemKeys(
|
||||||
|
itemKeyPrefix: ItemKeyPrefix,
|
||||||
|
): Promise<string[]> {
|
||||||
|
const storage = storageForPrefix(itemKeyPrefix);
|
||||||
const stored = Object.keys(await storage.get());
|
const stored = Object.keys(await storage.get());
|
||||||
const keys = stored.filter((key) => key.startsWith(itemKeyPrefix));
|
const keys = stored.filter((key) => key.startsWith(itemKeyPrefix));
|
||||||
return keys;
|
return keys;
|
||||||
|
@ -123,9 +131,11 @@ export async function getItemKeys(): Promise<string[]> {
|
||||||
*
|
*
|
||||||
* @returns The next ID as a number.
|
* @returns The next ID as a number.
|
||||||
*/
|
*/
|
||||||
export async function nextItemId(): Promise<number> {
|
export async function nextItemId(
|
||||||
|
itemKeyPrefix: ItemKeyPrefix = "item-",
|
||||||
|
): Promise<number> {
|
||||||
// Get all the item keys and sort them so the highest ID is first.
|
// Get all the item keys and sort them so the highest ID is first.
|
||||||
const keys = await getItemKeys();
|
const keys = await getItemKeys(itemKeyPrefix);
|
||||||
keys.sort((a, b) => b.localeCompare(a));
|
keys.sort((a, b) => b.localeCompare(a));
|
||||||
|
|
||||||
// Get the first key or use 0 if no items exist yet.
|
// Get the first key or use 0 if no items exist yet.
|
||||||
|
@ -142,8 +152,10 @@ export async function nextItemId(): Promise<number> {
|
||||||
* queue is empty.
|
* queue is empty.
|
||||||
*/
|
*/
|
||||||
export async function nextItem(): Promise<Value<Item> | undefined> {
|
export async function nextItem(): Promise<Value<Item> | undefined> {
|
||||||
|
const itemKeyPrefix: ItemKeyPrefix = "item-";
|
||||||
|
|
||||||
// Get all the item keys and sort them so the lowest ID is first.
|
// Get all the item keys and sort them so the lowest ID is first.
|
||||||
const keys = await getItemKeys();
|
const keys = await getItemKeys(itemKeyPrefix);
|
||||||
keys.sort((a, b) => a.localeCompare(b));
|
keys.sort((a, b) => a.localeCompare(b));
|
||||||
|
|
||||||
// If no keys exist then exit early.
|
// If no keys exist then exit early.
|
||||||
|
@ -166,7 +178,8 @@ export async function nextItem(): Promise<Value<Item> | undefined> {
|
||||||
* Set the WebExtension's badge text to show the current {@link Item} count.
|
* Set the WebExtension's badge text to show the current {@link Item} count.
|
||||||
*/
|
*/
|
||||||
export async function setBadgeText(): Promise<void> {
|
export async function setBadgeText(): Promise<void> {
|
||||||
const keys = await getItemKeys();
|
const itemKeyPrefix: ItemKeyPrefix = "item-";
|
||||||
|
const keys = await getItemKeys(itemKeyPrefix);
|
||||||
const count = keys.length;
|
const count = keys.length;
|
||||||
const action: browser.Action.Static =
|
const action: browser.Action.Static =
|
||||||
$browser === "firefox" ? browser.browserAction : browser.action;
|
$browser === "firefox" ? browser.browserAction : browser.action;
|
||||||
|
|
Loading…
Reference in New Issue