Implementar Web Share Target API para compartir fotos con la PWA
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:
2025-10-17 18:29:00 -06:00
parent ced637a7a9
commit 5bb5e5092e
17 changed files with 367 additions and 1 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

30
pwa_assets/index.html Normal file
View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- El manifest proporciona información sobre el nombre, iconos y URL de inicio de la app 【407831617096888†L240-L248】. -->
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#ffffff">
<title>DayNight PWA</title>
<link rel="icon" href="icons/icon-192x192.png" sizes="192x192">
</head>
<body>
<h1>Bienvenido a la app DayNight PWA</h1>
<p>Esta es una versión instalable de la app con soporte offline.</p>
<script>
// Registramos el service worker para habilitar caché y funcionalidad offline【407555884650804†L205-L213】.
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js')
.then(function(reg) {
console.log('Service worker registrado con scope:', reg.scope);
}).catch(function(err) {
console.error('Error al registrar el service worker:', err);
});
});
}
</script>
</body>
</html>

22
pwa_assets/manifest.json Normal file
View File

@@ -0,0 +1,22 @@
{
"name": "DayNight PWA",
"short_name": "DayNight",
"description": "Aplicación de ejemplo PWA instalable y offline.",
"id": "/",
"start_url": "/index.html",
"scope": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ffffff",
"icons": [
{ "src": "icons/icon-72x72.png", "sizes": "72x72", "type": "image/png" },
{ "src": "icons/icon-96x96.png", "sizes": "96x96", "type": "image/png" },
{ "src": "icons/icon-128x128.png", "sizes": "128x128", "type": "image/png" },
{ "src": "icons/icon-144x144.png", "sizes": "144x144", "type": "image/png" },
{ "src": "icons/icon-152x152.png", "sizes": "152x152", "type": "image/png" },
{ "src": "icons/icon-192x192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
{ "src": "icons/icon-256x256.png", "sizes": "256x256", "type": "image/png" },
{ "src": "icons/icon-384x384.png", "sizes": "384x384", "type": "image/png" },
{ "src": "icons/icon-512x512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }
]
}

65
pwa_assets/sw.js Normal file
View File

@@ -0,0 +1,65 @@
/*
Service worker para la app DayNight. Este script cachea recursos estáticos
durante la instalación para que se puedan servir sin conexión. Según MDN, en
el evento de instalación se deben abrir los caches y agregar recursos con
`addAll()`【407555884650804†L360-L410】, y luego interceptar las peticiones y
responder desde el cache cuando sea posible【407555884650804†L205-L213】.
*/
const CACHE_NAME = 'daynight-cache-v1';
const ASSETS = [
'/',
'/index.html',
'/manifest.json',
'/icons/icon-72x72.png',
'/icons/icon-96x96.png',
'/icons/icon-128x128.png',
'/icons/icon-144x144.png',
'/icons/icon-152x152.png',
'/icons/icon-192x192.png',
'/icons/icon-256x256.png',
'/icons/icon-384x384.png',
'/icons/icon-512x512.png'
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(ASSETS);
})
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((names) => {
return Promise.all(
names.map((name) => {
if (name !== CACHE_NAME) {
return caches.delete(name);
}
})
);
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
if (cached) {
return cached;
}
return fetch(event.request).then((networkResponse) => {
if (!networkResponse || networkResponse.status !== 200 || networkResponse.type !== 'basic') {
return networkResponse;
}
const responseClone = networkResponse.clone();
caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, responseClone);
});
return networkResponse;
});
})
);
});