diff --git a/frontend/src/stores/messageMedia.ts b/frontend/src/stores/messageMedia.ts new file mode 100644 index 0000000..3ef4661 --- /dev/null +++ b/frontend/src/stores/messageMedia.ts @@ -0,0 +1,39 @@ +import { defineStore } from 'pinia' + +import type { MediaType, PhoneMedia } from '@/types/media' + +type MessageMediaRequest = { + mediaType: MediaType + phoneNumber: string +} + +type MessageMediaResult = MessageMediaRequest & { + media: PhoneMedia +} + +export const useMessageMediaStore = defineStore('message-media', { + state: () => ({ + request: null as MessageMediaRequest | null, + result: null as MessageMediaResult | null, + }), + actions: { + begin(phoneNumber: string, mediaType: MediaType): void { + this.request = { mediaType, phoneNumber } + this.result = null + }, + cancel(): void { + this.request = null + }, + complete(media: PhoneMedia): void { + if (!this.request || this.request.mediaType !== media.mediaType) return + this.result = { ...this.request, media } + this.request = null + }, + consume(phoneNumber: string): PhoneMedia | null { + if (!this.result || this.result.phoneNumber !== phoneNumber) return null + const media = this.result.media + this.result = null + return media + }, + }, +}) diff --git a/frontend/src/views/apps/CameraApp.vue b/frontend/src/views/apps/CameraApp.vue index 7b9bdbc..a9ca7d2 100644 --- a/frontend/src/views/apps/CameraApp.vue +++ b/frontend/src/views/apps/CameraApp.vue @@ -15,8 +15,9 @@ import { ZapOff, } from 'lucide-vue-next' import { computed, onBeforeUnmount, onMounted, ref } from 'vue' -import { useRouter } from 'vue-router' +import { useRoute, useRouter } from 'vue-router' +import { useMessageMediaStore } from '@/stores/messageMedia' import { usePhoneStore } from '@/stores/phone' import type { MediaType, PhoneMedia, UploadResult } from '@/types/media' import { createGameView, type GameView } from '@/utils/gameView' @@ -33,8 +34,14 @@ type CaptureItem = { const isDevelopment = import.meta.env.DEV const zoomLevels = [0.5, 1, 2, 3] as const const phone = usePhoneStore() +const messageMedia = useMessageMediaStore() +const route = useRoute() const router = useRouter() -const mode = ref('photo') +const requestedMessageMedia = computed(() => { + const value = route.query.messageAttachment + return value === 'photo' || value === 'video' ? value : null +}) +const mode = ref(requestedMessageMedia.value ?? 'photo') const selectedZoom = ref<(typeof zoomLevels)[number]>(1) const flashEnabled = ref(false) const frontCamera = ref(false) @@ -307,6 +314,11 @@ function onMessage(event: MessageEvent): void { if (result.success && result.media) { latestMedia.value = result.media updateCapture(result.correlationId, { status: 'success' }) + if (requestedMessageMedia.value === result.media.mediaType) { + messageMedia.complete(result.media) + void router.replace('/apps/messages') + return + } showCameraNotice(phone.t('Apps.camera.saved')) window.setTimeout(() => { captures.value = captures.value.filter( @@ -473,7 +485,14 @@ onBeforeUnmount(() => { class="camera-latest" type="button" :aria-label="phone.t('Apps.camera.openGallery')" - @click="router.push('/apps/photos')" + @click=" + router.push({ + path: '/apps/photos', + query: requestedMessageMedia + ? { messageAttachment: requestedMessageMedia } + : undefined, + }) + " > (() => { + const value = route.query.messageAttachment + return value === 'photo' || value === 'video' ? value : null +}) const media = ref([]) -const filter = ref('all') +const filter = ref(requestedMessageMedia.value ?? 'all') const loading = ref(true) const fetching = ref(false) const hasMore = ref(true) @@ -193,6 +202,11 @@ function observeMore(): void { } function openMedia(entry: PhoneMedia): void { + if (requestedMessageMedia.value) { + messageMedia.complete(entry) + void router.replace('/apps/messages') + return + } landscapeViewer.value = false phone.setCameraLandscape(false) selected.value = entry @@ -200,6 +214,11 @@ function openMedia(entry: PhoneMedia): void { imagePan.value = { x: 0, y: 0 } } +function cancelMessageSelection(): void { + messageMedia.cancel() + void router.replace('/apps/messages') +} + function closeMedia(): void { landscapeViewer.value = false phone.setCameraLandscape(false) @@ -326,7 +345,15 @@ onBeforeUnmount(() => { class="gallery-page !pt-[44px]" :aria-label="phone.t('Apps.photos.name')" > - + + +