Implementar Web Share Target API para compartir fotos con la PWA
Some checks failed
build-and-deploy / build-and-deploy (push) Has been cancelled
Some checks failed
build-and-deploy / build-and-deploy (push) Has been cancelled
- Agregar share_target al manifest de la PWA - Crear endpoint /api/share-target para recibir archivos compartidos - Guardar archivos temporalmente en /public/temp-shared - Modificar UserProfileForm para aceptar imágenes externas - Detectar automáticamente imágenes compartidas y procesarlas - Crear endpoint /api/share-target/cleanup para limpiar temporales - Mostrar toast informativo al recibir imagen compartida - Redirigir automáticamente al formulario de perfil - Soportar compartir desde galería, otras apps, etc.
This commit is contained in:
@@ -19,7 +19,8 @@
|
||||
<!-- Formulario de edición de perfil o Lista de aplicaciones -->
|
||||
<UserProfileForm
|
||||
v-if="showProfileForm"
|
||||
@close="showProfileForm = false"
|
||||
:shared-image-url="sharedImageUrl"
|
||||
@close="handleCloseProfileForm"
|
||||
/>
|
||||
<AuthApplicationsList v-else />
|
||||
|
||||
@@ -51,10 +52,38 @@
|
||||
<script setup lang="ts">
|
||||
const { isAuthenticated } = useAuthentik()
|
||||
const { isNight } = useTheme()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// Estado para mostrar formulario de edición
|
||||
const showProfileForm = ref(false)
|
||||
|
||||
// URL de imagen compartida (si existe)
|
||||
const sharedImageUrl = ref<string | null>(null)
|
||||
|
||||
// Detectar si se compartió una imagen
|
||||
onMounted(() => {
|
||||
const sharedToken = route.query.shared as string
|
||||
const sharedFile = route.query.file as string
|
||||
|
||||
if (sharedToken && sharedFile) {
|
||||
// Construir URL del archivo temporal
|
||||
sharedImageUrl.value = `/temp-shared/${sharedFile}`
|
||||
|
||||
// Abrir automáticamente el formulario de perfil
|
||||
showProfileForm.value = true
|
||||
|
||||
// Limpiar query params de la URL sin recargar
|
||||
router.replace({ query: {} })
|
||||
}
|
||||
})
|
||||
|
||||
// Manejar cierre del formulario
|
||||
const handleCloseProfileForm = () => {
|
||||
showProfileForm.value = false
|
||||
sharedImageUrl.value = null
|
||||
}
|
||||
|
||||
// Configurar meta tags para PWA
|
||||
useHead({
|
||||
htmlAttrs: {
|
||||
|
||||
@@ -420,6 +420,11 @@
|
||||
const { user } = useAuthentik()
|
||||
const toast = useToast()
|
||||
|
||||
// Props
|
||||
const props = defineProps<{
|
||||
sharedImageUrl?: string | null
|
||||
}>()
|
||||
|
||||
// Emits
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
@@ -544,6 +549,13 @@ if (import.meta.client) {
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload)
|
||||
})
|
||||
|
||||
// Detectar y procesar imagen compartida automáticamente
|
||||
watch(() => props.sharedImageUrl, async (newUrl) => {
|
||||
if (newUrl && import.meta.client) {
|
||||
await processSharedImage(newUrl)
|
||||
}
|
||||
}, { immediate: true })
|
||||
}
|
||||
|
||||
// Enviar formulario
|
||||
@@ -802,6 +814,55 @@ const processImageFile = async (file: File) => {
|
||||
const blob = new Blob([file], { type: file.type })
|
||||
await handleAvatarCapture(blob)
|
||||
}
|
||||
|
||||
// Procesar imagen compartida desde Web Share Target
|
||||
const processSharedImage = async (imageUrl: string) => {
|
||||
try {
|
||||
// Mostrar notificación
|
||||
toast.add({
|
||||
title: 'Imagen compartida recibida',
|
||||
description: 'Procesando imagen para tu avatar...',
|
||||
color: 'info',
|
||||
icon: 'i-heroicons-photo'
|
||||
})
|
||||
|
||||
// Descargar la imagen del servidor
|
||||
const response = await fetch(imageUrl)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('No se pudo cargar la imagen compartida')
|
||||
}
|
||||
|
||||
const blob = await response.blob()
|
||||
|
||||
// Validar que sea una imagen
|
||||
if (!blob.type.startsWith('image/')) {
|
||||
throw new Error('El archivo compartido no es una imagen válida')
|
||||
}
|
||||
|
||||
// Procesar y subir la imagen
|
||||
await handleAvatarCapture(blob)
|
||||
|
||||
// Limpiar archivo temporal del servidor
|
||||
try {
|
||||
await $fetch('/api/share-target/cleanup', {
|
||||
method: 'POST',
|
||||
body: { imageUrl }
|
||||
})
|
||||
} catch (cleanupError) {
|
||||
console.error('Error limpiando archivo temporal:', cleanupError)
|
||||
// No es crítico si falla la limpieza
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error procesando imagen compartida:', error)
|
||||
toast.add({
|
||||
title: 'Error',
|
||||
description: error.message || 'No se pudo procesar la imagen compartida',
|
||||
color: 'error',
|
||||
icon: 'i-heroicons-exclamation-triangle'
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user