mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 17:23:25 +00:00
ADD - voice memo app
This commit is contained in:
@@ -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 }
|
||||
})
|
||||
@@ -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.',
|
||||
|
||||
Reference in New Issue
Block a user