diff --git a/app/src/broadcast.ts b/app/src/broadcast.ts index a2baa26..7eca8b4 100644 --- a/app/src/broadcast.ts +++ b/app/src/broadcast.ts @@ -1,5 +1,5 @@ import { onCleanup } from "solid-js"; -import { Group } from "./service-worker/schema"; +import { Group } from "./database/schema"; export const BROADCAST_NAME = "meal"; const broadcast = new BroadcastChannel(BROADCAST_NAME); diff --git a/app/src/database/index.ts b/app/src/database/index.ts new file mode 100644 index 0000000..344d7f8 --- /dev/null +++ b/app/src/database/index.ts @@ -0,0 +1,57 @@ +import { openDB } from "idb"; +import { Group, Schema } from "./schema"; + +export const openDatabase = openDB("meal", 1, { + upgrade(database) { + console.debug("Service worker: upgrading database"); + if (!database.objectStoreNames.contains("configuration")) { + database.createObjectStore("configuration", { autoIncrement: true }); + } + if (!database.objectStoreNames.contains("groups")) { + database.createObjectStore("groups", { + autoIncrement: true, + keyPath: "id", + }); + } + }, +}); + +openDatabase.then((database) => { + const transaction = database.transaction("configuration", "readwrite"); + const store = transaction.objectStore("configuration"); + if (store.add === undefined) throw new Error("Store is undefined"); + + store.add({ isOnboarded: false }); +}); + +export async function getConfiguration(): Promise { + // Get configuration + const database = await openDatabase; + const transaction = database.transaction("configuration", "readonly"); + const store = transaction.objectStore("configuration"); + // Get first item + const cursor = await store.openCursor(); + const configuration = cursor?.value as Schema["configuration"]; + return configuration; +} + +export async function getGroup(groupId: string): Promise { + const database = await openDatabase; + const transaction = database.transaction("groups", "readonly"); + const store = transaction.objectStore("groups"); + return (await store.get(groupId)) as Group; +} + +export async function updateGroup(group: Group) { + const database = await openDatabase; + const transaction = database.transaction("groups", "readwrite"); + const store = transaction.objectStore("groups"); + store.put(group); +} + +export async function insertGroup(group: Group) { + const database = await openDatabase; + const transaction = database.transaction("groups", "readwrite"); + const store = transaction.objectStore("groups"); + store.add(group); +} diff --git a/app/src/service-worker/schema.d.ts b/app/src/database/schema.d.ts similarity index 100% rename from app/src/service-worker/schema.d.ts rename to app/src/database/schema.d.ts diff --git a/app/src/routes/chat.jsx b/app/src/routes/chat.jsx index 4285de3..c53e132 100644 --- a/app/src/routes/chat.jsx +++ b/app/src/routes/chat.jsx @@ -2,9 +2,7 @@ import { Navigate, useParams } from "@solidjs/router"; import { createMemo, For, Show } from "solid-js"; import { useAppContext } from "../components/AppContext"; import { sendMessage } from "../sendMessage"; -//@ts-expect-error TS6192 Can not handle new JSDoc syntax (yet?) /** @import { JSX } from "solid-js" */ -//@ts-expect-error TS6192 Can not handle new JSDoc syntax (yet?) /** @import { Message } from "../components/AppContext" */ // TODO take this inspiration https://firebasestorage.googleapis.com/v0/b/design-spec/o/projects%2Fgoogle-material-3%2Fimages%2Fly7219l1-1.png?alt=media&token=67ff316b-7515-4e9f-9971-4e580290b1f2 diff --git a/app/src/routes/invite.jsx b/app/src/routes/invite.jsx index 312e4a7..96249d7 100644 --- a/app/src/routes/invite.jsx +++ b/app/src/routes/invite.jsx @@ -5,6 +5,7 @@ import QRCode from "qrcode"; import { setupCrackle } from "../useCrackle"; import { useBroadcast } from "../broadcast"; import { ROUTES } from "."; +import { getConfiguration } from "../database"; /** @import { JSX, VoidProps, Signal } from "solid-js" */ const FormElement = /** @type {const} */ ({ @@ -69,11 +70,6 @@ function InviteSettingsDialog({ userName, ref }) { ); } -async function getConfiguration() { - const handle = await setupCrackle; - return await handle.getConfiguration(); -} - export default function Invite() { const navigate = useNavigate(); diff --git a/app/src/routes/join.tsx b/app/src/routes/join.tsx index 3a8abdd..cf7c490 100644 --- a/app/src/routes/join.tsx +++ b/app/src/routes/join.tsx @@ -2,6 +2,7 @@ import { Navigate, useNavigate, useParams } from "@solidjs/router"; import { createResource, JSX, Match, Switch } from "solid-js"; import { setupCrackle } from "../useCrackle"; import { ROUTES } from "."; +import { getConfiguration } from "../database"; async function decodeKeyPackage(encodedInvite: string) { const handle = await setupCrackle; @@ -22,10 +23,7 @@ export default function Join() { const [keyPackage] = createResource(parameters.package, decodeKeyPackage); - const [configuration] = createResource(async () => { - const handle = await setupCrackle; - return await handle.getConfiguration(); - }); + const [configuration] = createResource(getConfiguration); const navigate = useNavigate(); diff --git a/app/src/service-worker/operation.d.ts b/app/src/service-worker/operation.d.ts deleted file mode 100644 index 0b6a8ef..0000000 --- a/app/src/service-worker/operation.d.ts +++ /dev/null @@ -1,54 +0,0 @@ -export type Operation = - | "WriteMlsJoinConfiguration" - | "AppendOwnLeafNode" - | "QueueProposal" - | "WriteTree" - | "WriteInterimTranscriptHash" - | "WriteGroupContext" - | "WriteConfirmationTag" - | "WriteGroupState" - | "WriteMessageSecrets" - | "WriteResumptionPreSharedKeyStore" - | "WriteOwnLeafIndex" - | "WriteGroupEpochSecrets" - | "WriteSignatureKeyPair" - | "WriteEncryptionKeyPair" - | "WriteEncryptionEpochKeyPairs" - | "WriteKeyPackage" - | "WritePreSharedKey" - | "ReadMlsGroupJoinConfiguration" - | "ReadOwnLeafNodes" - | "ReadQueuedProposalReferences" - | "ReadQueuedProposals" - | "ReadTree" - | "ReadGroupContext" - | "ReadInterimTranscriptHash" - | "ReadConfirmationTag" - | "ReadGroupState" - | "ReadMessageSecrets" - | "ReadResumptionPskStore" - | "ReadOwnLeafIndex" - | "ReadGroupEpochSecrets" - | "ReadSignatureKeyPair" - | "ReadEncryptionKeyPair" - | "ReadEncryptionEpochKeyPairs" - | "ReadKeyPackage" - | "ReadPreSharedKey" - | "RemoveProposal" - | "DeleteOwnLeafNodes" - | "DeleteGroupConfiguration" - | "DeleteTree" - | "DeleteConfirmationTag" - | "DeleteGroupState" - | "DeleteContext" - | "DeleteInterimTranscriptHash" - | "DeleteMessageSecrets" - | "DeleteAllResumptionPreSharedKeySecrets" - | "DeleteOwnLeafIndex" - | "DeleteGroupEpochSecrets" - | "ClearProposalQueue" - | "DeleteSignatureKeyPair" - | "DeleteEncryptionKeyPair" - | "DeleteEncryptionEpochKeyPairs" - | "DeleteKeyPackage" - | "DeletePreSharedKey"; diff --git a/app/src/service-worker/serviceWorker.ts b/app/src/service-worker/serviceWorker.ts index 9cf1630..fdac686 100644 --- a/app/src/service-worker/serviceWorker.ts +++ b/app/src/service-worker/serviceWorker.ts @@ -1,9 +1,15 @@ import { precacheAndRoute } from "workbox-precaching"; -import { openDB } from "idb"; import init, { Client, DecodedPackage, Friend } from "meal-core"; import { expose } from "../crackle"; -import { Group, Message, Schema } from "./schema"; +import { Group, Message } from "../database/schema"; import { broadcastMessage } from "../broadcast"; +import { + getConfiguration, + getGroup, + insertGroup, + openDatabase, + updateGroup, +} from "../database"; /** * @import { Schema } from "./schema" @@ -18,39 +24,6 @@ console.debug("Service worker: environment", process.env.NODE_ENV); // @ts-expect-error This variable is replaced by workbox through vite pwa plugin precacheAndRoute(self.__WB_MANIFEST); -const openDatabase = /** @type {typeof openDB} */ openDB("meal", 1, { - upgrade(database) { - console.debug("Service worker: upgrading database"); - if (!database.objectStoreNames.contains("configuration")) { - database.createObjectStore("configuration", { autoIncrement: true }); - } - if (!database.objectStoreNames.contains("groups")) { - database.createObjectStore("groups", { - autoIncrement: true, - keyPath: "id", - }); - } - }, -}); - -openDatabase.then((database) => { - const transaction = database.transaction("configuration", "readwrite"); - const store = transaction.objectStore("configuration"); - if (store.add === undefined) throw new Error("Store is undefined"); - - store.add({ isOnboarded: false }); -}); - -async function getConfiguration(): Promise { - // Get configuration - const database = await openDatabase; - const transaction = database.transaction("configuration", "readonly"); - const store = transaction.objectStore("configuration"); - // Get first item - const cursor = await store.openCursor(); - const configuration = /** @type {Schema["configuration"]} */ cursor?.value; - return configuration; -} /** * Convenience function to make get a file from a directory more ergonomic without failing if the file does not exist. */ @@ -155,27 +128,6 @@ function assertNotShared( throw new Error("Uint8Array uses a SharedArrayBuffer"); } -async function getGroup(groupId: string): Promise { - const database = await openDatabase; - const transaction = database.transaction("groups", "readonly"); - const store = transaction.objectStore("groups"); - return await store.get(groupId); -} - -async function updateGroup(group: Group) { - const database = await openDatabase; - const transaction = database.transaction("groups", "readwrite"); - const store = transaction.objectStore("groups"); - store.put(group); -} - -async function insertGroup(group: Group) { - const database = await openDatabase; - const transaction = database.transaction("groups", "readwrite"); - const store = transaction.objectStore("groups"); - store.add(group); -} - const handler = { async getIsOnboarded() { console.debug("[Service worker] Getting isOnboarded"); @@ -183,7 +135,6 @@ const handler = { return configuration.isOnboarded; }, - getConfiguration, async setName(name: string) { const database = await openDatabase; const transaction = database.transaction("configuration", "readwrite");