feat: Aufnahme und lokaler Speicher für Sprachnotizen (T7 L5 Task 17)
Recorder (webm/opus oder mp4, 24 kbit/s, Warnung bei 150 s, Stopp bei 180 s oder im Hintergrund; Mikrofon und Wake-Lock werden in jedem Fall freigegeben, auch bei Fehlern und ausgestecktem Mikrofon) und IndexedDB v6 mit Store pending-voice (Index ownerUserId); volle Speicherquote ergibt VoiceStorageFullError ohne Datenverlust; Abmelden löscht nur die eigenen Notizen. Refs #27 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
1dc39f7658
commit
ce472164b5
6 files changed
+829
-11
No files matched your search
@@ -95,7 +95,7 @@ describe("offline queue database", () => {
|
||||
it.each([
|
||||
["a fresh install (0)", 0],
|
||||
["version 1", 1],
|
||||
])("opening from %s creates the plant report store at version 5", async (_name, from) => {
|
||||
])("opening from %s creates the plant report store at version 6", async (_name, from) => {
|
||||
if (from === 1) {
|
||||
const old = await openDB(DB_NAME, 1, {
|
||||
upgrade(database) {
|
||||
@@ -120,7 +120,7 @@ describe("offline queue database", () => {
|
||||
);
|
||||
|
||||
const upgraded = await openDB(DB_NAME);
|
||||
expect(upgraded.version).toBe(5);
|
||||
expect(upgraded.version).toBe(6);
|
||||
expect([...upgraded.objectStoreNames]).toContain("pending-phenology-reports");
|
||||
expect(await upgraded.count("pending-phenology-reports")).toBe(1);
|
||||
expect(await upgraded.count("pending-inspections")).toBe(from === 1 ? 1 : 0);
|
||||
@@ -166,11 +166,11 @@ describe("offline queue database", () => {
|
||||
expect((await db.listQueue("user-1")).map((item) => item.label)).toContain("Pflanzenmeldung");
|
||||
|
||||
const upgraded = await openDB(DB_NAME);
|
||||
expect(upgraded.version).toBe(5);
|
||||
expect(upgraded.version).toBe(6);
|
||||
upgraded.close();
|
||||
});
|
||||
|
||||
it("upgrading v2 → v5 keeps already queued inspections", async () => {
|
||||
it("upgrading v2 → v6 keeps already queued inspections", async () => {
|
||||
await createVersion2Queue({
|
||||
input: INSPECTION,
|
||||
idempotencyKey: "key-from-v2",
|
||||
@@ -192,7 +192,7 @@ describe("offline queue database", () => {
|
||||
expect(await db.summarizeQueue("user-1")).toEqual({ own: 1, failed: 0, foreign: 0 });
|
||||
|
||||
const upgraded = await openDB(DB_NAME);
|
||||
expect(upgraded.version).toBe(5);
|
||||
expect(upgraded.version).toBe(6);
|
||||
expect([...upgraded.objectStoreNames]).toEqual(
|
||||
expect.arrayContaining([
|
||||
...VERSION_2_STORES,
|
||||
@@ -237,7 +237,7 @@ describe("offline queue database", () => {
|
||||
expect(hive).toMatchObject({ idempotencyKey: "key-h", ownerUserId: "user-1" });
|
||||
|
||||
const upgraded = await openDB(DB_NAME);
|
||||
expect(upgraded.version).toBe(5);
|
||||
expect(upgraded.version).toBe(6);
|
||||
expect(await upgraded.count("pending-inspections")).toBe(1);
|
||||
expect(await upgraded.count("pending-phenology-reports")).toBe(1);
|
||||
expect([...upgraded.objectStoreNames]).toEqual(
|
||||
@@ -246,6 +246,69 @@ describe("offline queue database", () => {
|
||||
upgraded.close();
|
||||
});
|
||||
|
||||
it("upgrading to the new version keeps queued inspections, photos and phenology reports", async () => {
|
||||
const old = await openDB(DB_NAME, 5, {
|
||||
upgrade(database) {
|
||||
for (const store of [
|
||||
...VERSION_2_STORES,
|
||||
"pending-bulk",
|
||||
"pending-phenology-reports",
|
||||
"pending-hive-changes",
|
||||
]) {
|
||||
database.createObjectStore(store, { keyPath: "id", autoIncrement: true });
|
||||
}
|
||||
const photos = database.createObjectStore("pending-photos", { keyPath: "id" });
|
||||
photos.createIndex("sessionId", "sessionId");
|
||||
photos.createIndex("inspectionIdempotencyKey", "inspectionIdempotencyKey");
|
||||
photos.createIndex("ownerUserId", "ownerUserId");
|
||||
database.createObjectStore("offline-packs", { keyPath: "apiaryId" });
|
||||
database.createObjectStore("meta");
|
||||
},
|
||||
});
|
||||
await old.add("pending-inspections", { input: INSPECTION, ownerUserId: "user-1", queuedAt: "2026-10-01T08:00:00.000Z" });
|
||||
await old.add("pending-phenology-reports", { input: {}, ownerUserId: "user-1", queuedAt: "2026-10-01T09:00:00.000Z" });
|
||||
await old.add("pending-hive-changes", { input: {}, ownerUserId: "user-1", queuedAt: "2026-10-01T10:00:00.000Z" });
|
||||
await old.put("pending-photos", photo("p1", "key-1"));
|
||||
old.close();
|
||||
|
||||
const db = await loadDb();
|
||||
expect(await db.getPendingEntries("pending-inspections")).toHaveLength(1);
|
||||
|
||||
const upgraded = await openDB(DB_NAME);
|
||||
expect(upgraded.version).toBe(6);
|
||||
expect(await upgraded.count("pending-phenology-reports")).toBe(1);
|
||||
expect(await upgraded.count("pending-hive-changes")).toBe(1);
|
||||
expect(await upgraded.count("pending-photos")).toBe(1);
|
||||
expect([...upgraded.objectStoreNames]).toContain("pending-voice");
|
||||
expect([...upgraded.transaction("pending-voice").store.indexNames]).toEqual(["ownerUserId"]);
|
||||
upgraded.close();
|
||||
});
|
||||
|
||||
it("clearQueueOf removes one owner's voice notes and keeps everyone else's", async () => {
|
||||
const db = await loadDb();
|
||||
await db.getDb();
|
||||
const raw = await openDB(DB_NAME);
|
||||
const note = (id: string, ownerUserId: string) => ({
|
||||
id,
|
||||
ownerUserId,
|
||||
blob: new Blob(["x"]),
|
||||
mime: "audio/webm;codecs=opus",
|
||||
durationSec: 5,
|
||||
colonyId: "c-1",
|
||||
inspectionDate: "2026-10-16",
|
||||
createdAt: "2026-10-16T08:00:00.000Z",
|
||||
uploadIdempotencyKey: `u-${id}`,
|
||||
state: "recorded",
|
||||
});
|
||||
await raw.put("pending-voice", note("v1", "user-1") as never);
|
||||
await raw.put("pending-voice", note("v2", "user-2") as never);
|
||||
|
||||
await db.clearQueueOf("user-1");
|
||||
|
||||
expect(await raw.getAllKeys("pending-voice")).toEqual(["v2"]);
|
||||
raw.close();
|
||||
});
|
||||
|
||||
it("lists a hive change under its own name and marks inspections that carry one", async () => {
|
||||
const db = await loadDb();
|
||||
await db.queueHiveChange(
|
||||
|
||||
Reference in new issue
Block a user