ADD - voice memo app

This commit is contained in:
Leon.Schmidt
2026-08-13 16:16:32 +02:00
parent 223cb4a600
commit f4ed86fd63
29 changed files with 3234 additions and 50 deletions
+52
View File
@@ -0,0 +1,52 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { MemoDto, MemoUpdate } from '@/types/memos'
import { isMemoDto } from '@/types/memos'
import { nuiCall, type NuiResponse } from '@/utils/nui'
export const useMemosStore = defineStore('memos', () => {
const memos = ref<MemoDto[]>([])
const loading = ref(false)
function hydrate(items: MemoDto[]): void {
memos.value = Array.isArray(items) ? items.filter(isMemoDto) : []
}
function upsert(memo: MemoDto): void {
const index = memos.value.findIndex((item) => item.id === memo.id)
if (index >= 0) memos.value[index] = memo
else memos.value.unshift(memo)
memos.value.sort((left, right) => right.updatedAt - left.updatedAt)
}
async function load(): Promise<NuiResponse<MemoDto[]>> {
loading.value = true
try {
const response = await nuiCall<MemoDto[]>('memos:list')
if (response.success && Array.isArray(response.data)) hydrate(response.data)
return response
} finally {
loading.value = false
}
}
async function update(
id: string,
changes: MemoUpdate,
): Promise<NuiResponse<MemoDto>> {
const response = await nuiCall<MemoDto>('memos:update', { id, ...changes })
if (response.success && isMemoDto(response.data)) upsert(response.data)
return response
}
async function deleteMemo(id: string): Promise<NuiResponse<{ id: string }>> {
const response = await nuiCall<{ id: string }>('memos:delete', { id })
if (response.success) {
memos.value = memos.value.filter((memo) => memo.id !== id)
}
return response
}
return { deleteMemo, hydrate, load, loading, memos, update, upsert }
})
+67 -2
View File
@@ -33,6 +33,7 @@ export type PhoneOpenPayload = {
device?: PhoneDevice
lang?: string
locales?: LocaleTree
memos?: DeviceBootstrap['memos']
notes?: DeviceBootstrap['notes']
security?: DeviceSecurity
token?: string
@@ -3434,6 +3435,71 @@ const defaultLocales: LocaleTree = {
unpin: 'Unpin note',
deleteNote: 'Delete note',
},
memos: {
name: 'Memos',
memo: 'Voice Memo',
back: 'Back',
actions: 'Memo actions',
newMemo: 'New Recording',
newMemoWithDate: 'New Recording · {date}',
searchPlaceholder: 'Search Recordings',
emptyTitle: 'No Recordings',
emptyBody: 'Tap the record button to capture your first voice memo.',
noResults: 'No Results',
noResultsBody: 'Try searching for a different title or note.',
recording: 'Recording',
paused: 'Paused',
preparing: 'Preparing microphone…',
saving: 'Saving Recording…',
stop: 'Stop',
pause: 'Pause',
resume: 'Resume',
cancel: 'Cancel',
done: 'Done',
play: 'Play recording',
pausePlayback: 'Pause recording',
skipBack: 'Back 15 seconds',
skipForward: 'Forward 15 seconds',
playbackSpeed: 'Playback Speed',
title: 'Title',
titlePlaceholder: 'Recording title',
note: 'Note',
notePlaceholder: 'Add details…',
delete: 'Delete memo',
deleteTitle: 'Delete Voice Memo?',
deleteBody: 'This recording will be permanently deleted.',
discardTitle: 'Discard Recording?',
discardBody: 'The current recording will not be saved.',
discard: 'Discard',
keepRecording: 'Keep Recording',
deleted: 'Memo deleted.',
microphoneUnavailable: 'The microphone is unavailable.',
recordingTooLarge: 'The recording is too large to save.',
maximumDuration: 'The maximum recording duration was reached.',
errors: {
conflict: 'This memo changed on another device. It has been reloaded.',
invalid_memo: 'The voice memo contains invalid data.',
invalid_request: 'The memo request is invalid.',
invalid_upload: 'The uploaded recording could not be verified.',
invalid_upload_token: 'The recording upload could not be verified.',
memo_limit: 'The limit of 100 voice memos has been reached.',
memo_not_found: 'This voice memo no longer exists.',
media_provider_failed: 'The voice memo upload service is unavailable.',
media_provider_rate_limited:
'The voice memo upload service is busy. Try again shortly.',
media_provider_unauthorized:
'The configured FiveManage API key was rejected.',
missing_config: 'Voice memo uploads are not configured.',
operation_in_progress: 'Finish the current recording first.',
owner_changed: 'The active phone changed while saving the memo.',
rate_limited: 'Too many memo changes. Try again shortly.',
recording_failed: 'The recording could not be captured.',
request_timeout: 'The memo request timed out.',
upload_failed: 'The recording could not be uploaded.',
upload_timeout: 'The recording upload timed out.',
request_failed: 'Memos are temporarily unavailable.',
},
},
photos: {
name: 'Gallery',
count: '{count} items',
@@ -3457,8 +3523,7 @@ const defaultLocales: LocaleTree = {
title: 'Import',
chooseSource: 'Choose a website',
linkTitle: 'Import from Link',
linkBody:
'Paste a direct link to an image or video from this website.',
linkBody: 'Paste a direct link to an image or video from this website.',
linkLabel: 'Image or video link',
linkPlaceholder: 'https://...',
linkCompleted: 'Media imported.',