Desktop: Abrir app-items en nueva ventana para lanzar PWAs
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 58s
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 58s
- Crear composable useDeviceType para detectar desktop/móvil - Modificar ApplicationsList para usar target='_blank' en desktop - Ajustar pwa-link-handler para permitir nuevas ventanas en desktop - En móvil mantener comportamiento original según openInNewTab
This commit is contained in:
@@ -71,7 +71,7 @@
|
|||||||
v-for="app in filteredApplications"
|
v-for="app in filteredApplications"
|
||||||
:key="app.pk"
|
:key="app.pk"
|
||||||
:href="app.launchUrl"
|
:href="app.launchUrl"
|
||||||
:target="app.openInNewTab ? '_blank' : '_self'"
|
:target="shouldOpenInNewTab(app) ? '_blank' : '_self'"
|
||||||
class="app-card"
|
class="app-card"
|
||||||
>
|
>
|
||||||
<div class="app-card-content">
|
<div class="app-card-content">
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
{{ app.name }}
|
{{ app.name }}
|
||||||
</h4>
|
</h4>
|
||||||
<UIcon
|
<UIcon
|
||||||
v-if="app.openInNewTab"
|
v-if="shouldOpenInNewTab(app)"
|
||||||
name="i-heroicons-arrow-top-right-on-square"
|
name="i-heroicons-arrow-top-right-on-square"
|
||||||
class="external-icon"
|
class="external-icon"
|
||||||
/>
|
/>
|
||||||
@@ -137,6 +137,9 @@ interface Application {
|
|||||||
openInNewTab: boolean
|
openInNewTab: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detectar si estamos en desktop
|
||||||
|
const { isDesktop } = useDeviceType()
|
||||||
|
|
||||||
const { data: applicationsRaw, pending, error, refresh } = await useFetch<Application[]>('/api/authentik/applications', {
|
const { data: applicationsRaw, pending, error, refresh } = await useFetch<Application[]>('/api/authentik/applications', {
|
||||||
default: () => []
|
default: () => []
|
||||||
})
|
})
|
||||||
@@ -304,6 +307,13 @@ const refreshInterval = setInterval(() => {
|
|||||||
refresh()
|
refresh()
|
||||||
}, 5 * 60 * 1000)
|
}, 5 * 60 * 1000)
|
||||||
|
|
||||||
|
// Determinar si un enlace debe abrirse en nueva ventana
|
||||||
|
// En desktop, siempre abrir en nueva ventana para lanzar PWAs
|
||||||
|
// En móvil, respetar la configuración de la app
|
||||||
|
const shouldOpenInNewTab = (app: Application): boolean => {
|
||||||
|
return isDesktop.value || app.openInNewTab
|
||||||
|
}
|
||||||
|
|
||||||
// Limpiar interval cuando el componente se desmonte
|
// Limpiar interval cuando el componente se desmonte
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearInterval(refreshInterval)
|
clearInterval(refreshInterval)
|
||||||
|
|||||||
33
nuxt4/app/composables/useDeviceType.ts
Normal file
33
nuxt4/app/composables/useDeviceType.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/**
|
||||||
|
* Composable para detectar el tipo de dispositivo (desktop o móvil)
|
||||||
|
* Usa matchMedia para detectar el tamaño de pantalla
|
||||||
|
*/
|
||||||
|
export const useDeviceType = () => {
|
||||||
|
const isDesktop = ref(false)
|
||||||
|
|
||||||
|
if (import.meta.client && typeof window !== 'undefined') {
|
||||||
|
// Detectar si el dispositivo es desktop basado en el ancho de pantalla
|
||||||
|
// Usamos 1024px como punto de corte (típico para tablets/desktops)
|
||||||
|
const mediaQuery = window.matchMedia('(min-width: 1024px)')
|
||||||
|
|
||||||
|
// Establecer valor inicial
|
||||||
|
isDesktop.value = mediaQuery.matches
|
||||||
|
|
||||||
|
// Escuchar cambios en el tamaño de pantalla
|
||||||
|
const handleChange = (e: MediaQueryListEvent) => {
|
||||||
|
isDesktop.value = e.matches
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaQuery.addEventListener('change', handleChange)
|
||||||
|
|
||||||
|
// Cleanup al desmontar
|
||||||
|
onUnmounted(() => {
|
||||||
|
mediaQuery.removeEventListener('change', handleChange)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isDesktop: readonly(isDesktop),
|
||||||
|
isMobile: computed(() => !isDesktop.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* Plugin para interceptar enlaces externos que apuntan a esta PWA
|
* Plugin para interceptar enlaces externos que apuntan a esta PWA
|
||||||
* y navegarlos dentro de la misma ventana en lugar de abrir nuevas ventanas/pestañas
|
* y navegarlos dentro de la misma ventana en lugar de abrir nuevas ventanas/pestañas
|
||||||
|
* En desktop, permite que los enlaces con target="_blank" abran nuevas ventanas para
|
||||||
|
* lanzar PWAs correspondientes
|
||||||
*/
|
*/
|
||||||
export default defineNuxtPlugin(() => {
|
export default defineNuxtPlugin(() => {
|
||||||
if (import.meta.client && typeof window !== 'undefined') {
|
if (import.meta.client && typeof window !== 'undefined') {
|
||||||
@@ -9,6 +11,9 @@ export default defineNuxtPlugin(() => {
|
|||||||
window.matchMedia('(display-mode: window-controls-overlay)').matches ||
|
window.matchMedia('(display-mode: window-controls-overlay)').matches ||
|
||||||
(window.navigator as any).standalone === true
|
(window.navigator as any).standalone === true
|
||||||
|
|
||||||
|
// Detectar si estamos en desktop
|
||||||
|
const isDesktop = window.matchMedia('(min-width: 1024px)').matches
|
||||||
|
|
||||||
if (isPWA) {
|
if (isPWA) {
|
||||||
// Obtener el origin de la aplicación
|
// Obtener el origin de la aplicación
|
||||||
const appOrigin = window.location.origin
|
const appOrigin = window.location.origin
|
||||||
@@ -31,8 +36,12 @@ export default defineNuxtPlugin(() => {
|
|||||||
// Solo interceptar enlaces que apunten a este origin
|
// Solo interceptar enlaces que apunten a este origin
|
||||||
if (url.origin === appOrigin) {
|
if (url.origin === appOrigin) {
|
||||||
// Si el enlace tiene target="_blank", no interceptar
|
// Si el enlace tiene target="_blank", no interceptar
|
||||||
|
// Esto permite que en desktop los enlaces abran nuevas ventanas (PWAs)
|
||||||
const linkTarget = link.getAttribute('target')
|
const linkTarget = link.getAttribute('target')
|
||||||
if (linkTarget === '_blank') return
|
if (linkTarget === '_blank') {
|
||||||
|
console.debug('Permitiendo apertura de nueva ventana para:', href)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Prevenir comportamiento por defecto
|
// Prevenir comportamiento por defecto
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
@@ -51,6 +60,7 @@ export default defineNuxtPlugin(() => {
|
|||||||
}, { capture: true })
|
}, { capture: true })
|
||||||
|
|
||||||
// Interceptar window.open para links que apunten a esta app
|
// Interceptar window.open para links que apunten a esta app
|
||||||
|
// En desktop, permitimos nuevas ventanas con target="_blank"
|
||||||
const originalWindowOpen = window.open
|
const originalWindowOpen = window.open
|
||||||
window.open = function(url?: string | URL, target?: string, features?: string) {
|
window.open = function(url?: string | URL, target?: string, features?: string) {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
@@ -61,12 +71,19 @@ export default defineNuxtPlugin(() => {
|
|||||||
const urlObj = typeof url === 'string' ? new URL(url, window.location.origin) : url
|
const urlObj = typeof url === 'string' ? new URL(url, window.location.origin) : url
|
||||||
|
|
||||||
// Si es un enlace a esta app y no tiene target especificado o es _self
|
// Si es un enlace a esta app y no tiene target especificado o es _self
|
||||||
|
// Y NO estamos en desktop con target _blank (para permitir lanzar PWAs)
|
||||||
if (urlObj.origin === appOrigin && (!target || target === '_self')) {
|
if (urlObj.origin === appOrigin && (!target || target === '_self')) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const relativePath = urlObj.pathname + urlObj.search + urlObj.hash
|
const relativePath = urlObj.pathname + urlObj.search + urlObj.hash
|
||||||
router.push(relativePath)
|
router.push(relativePath)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// En desktop con target="_blank", permitir la apertura normal
|
||||||
|
if (isDesktop && target === '_blank') {
|
||||||
|
console.debug('Permitiendo window.open en desktop para:', url)
|
||||||
|
return originalWindowOpen.call(window, url, target, features)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.debug('Error parsing window.open URL:', error)
|
console.debug('Error parsing window.open URL:', error)
|
||||||
}
|
}
|
||||||
@@ -75,7 +92,7 @@ export default defineNuxtPlugin(() => {
|
|||||||
return originalWindowOpen.call(window, url, target, features)
|
return originalWindowOpen.call(window, url, target, features)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ PWA link handler initialized - capturing internal links')
|
console.log('✅ PWA link handler initialized - capturing internal links (desktop mode:', isDesktop, ')')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user