Files
kapteins-daagbok/client/src/services/voiceAttachments.ts
T
elpatron b7a9df6ae0 refactor: drop redundant Live Log photo/voice Plausible events
Live-journal uploads are tracked only via Photo Uploaded and Voice Memo Uploaded with context live_log.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 15:40:22 +02:00

101 lines
2.7 KiB
TypeScript

import { db } from './db.js'
import { getActiveMasterKey } from './auth.js'
import { getLogbookKey } from './logbookKeys.js'
import { encryptJson } from './crypto.js'
import { syncLogbook } from './sync.js'
import { PlausibleEvents, trackPlausibleEvent } from './analytics.js'
async function getEncryptionKey(logbookId: string): Promise<ArrayBuffer> {
const key = await getLogbookKey(logbookId) || getActiveMasterKey()
if (!key) throw new Error('Encryption key not found. Please log in.')
return key
}
export async function saveEntryVoiceMemo(options: {
logbookId: string
entryId: string
audioDataUrl: string
mimeType: string
durationSec: number
caption?: string
analyticsContext?: string
}): Promise<string> {
const {
logbookId,
entryId,
audioDataUrl,
mimeType,
durationSec,
caption = '',
analyticsContext = 'logbook'
} = options
const masterKey = await getEncryptionKey(logbookId)
const voiceId = window.crypto.randomUUID()
const voicePayload = {
audio: audioDataUrl,
mimeType,
durationSec,
caption: caption.trim()
}
const encrypted = await encryptJson(voicePayload, masterKey)
const now = new Date().toISOString()
await db.voiceMemos.put({
payloadId: voiceId,
entryId,
logbookId,
encryptedData: encrypted.ciphertext,
iv: encrypted.iv,
tag: encrypted.tag,
updatedAt: now
})
await db.syncQueue.put({
action: 'create',
type: 'voiceMemo',
payloadId: voiceId,
logbookId,
data: JSON.stringify({
encryptedData: encrypted.ciphertext,
iv: encrypted.iv,
tag: encrypted.tag,
entryId
}),
updatedAt: now
})
trackPlausibleEvent(PlausibleEvents.VOICE_MEMO_UPLOADED, { context: analyticsContext })
syncLogbook(logbookId).catch((err) => console.warn('Background sync failed:', err))
return voiceId
}
export async function deleteEntryVoiceMemo(logbookId: string, voiceId: string): Promise<void> {
const now = new Date().toISOString()
await db.voiceMemos.delete(voiceId)
await db.syncQueue.put({
action: 'delete',
type: 'voiceMemo',
payloadId: voiceId,
logbookId,
data: '',
updatedAt: now
})
syncLogbook(logbookId).catch((err) => console.warn('Background sync failed:', err))
}
/** Deletes the newest voice memo for an entry; returns its id or null. */
export async function removeLastVoiceMemoForEntry(
logbookId: string,
entryId: string
): Promise<string | null> {
const memos = await db.voiceMemos.where({ entryId }).toArray()
if (memos.length === 0) return null
memos.sort(
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
)
const lastId = memos[0].payloadId
await deleteEntryVoiceMemo(logbookId, lastId)
return lastId
}