Files
sgse-app/apps/web/src/lib/components/PushNotificationManager.svelte

137 lines
4.1 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { useConvexClient } from 'convex-svelte';
import { api } from '@sgse-app/backend/convex/_generated/api';
import { useQuery } from 'convex-svelte';
import {
solicitarPushSubscription,
subscriptionToJSON,
removerPushSubscription
} from '$lib/utils/notifications';
const client = useConvexClient();
const currentUser = useQuery(api.auth.getCurrentUser, {});
// Capturar erros de Promise não tratados relacionados a message channel
// Este erro geralmente vem de extensões do Chrome ou comunicação com Service Worker
if (typeof window !== 'undefined') {
window.addEventListener(
'unhandledrejection',
(event: PromiseRejectionEvent) => {
const reason = event.reason;
const errorMessage = reason?.message || reason?.toString() || '';
// Filtrar apenas erros relacionados a message channel fechado
if (
errorMessage.includes('message channel closed') ||
errorMessage.includes('asynchronous response') ||
(errorMessage.includes('message channel') && errorMessage.includes('closed'))
) {
// Prevenir que o erro apareça no console
event.preventDefault();
// Silenciar o erro - é geralmente causado por extensões do Chrome
return false;
}
},
{ capture: true }
);
}
onMount(async () => {
let checkAuth: ReturnType<typeof setInterval> | null = null;
let mounted = true;
// Aguardar usuário estar autenticado
checkAuth = setInterval(async () => {
if (currentUser?.data && mounted) {
clearInterval(checkAuth!);
checkAuth = null;
try {
await registrarPushSubscription();
} catch (error) {
// Silenciar erros de push subscription para evitar spam no console
if (error instanceof Error && !error.message.includes('message channel')) {
console.error('Erro ao configurar push notifications:', error);
}
}
}
}, 500);
// Limpar intervalo após 30 segundos (timeout)
const timeout = setTimeout(() => {
if (checkAuth) {
clearInterval(checkAuth);
checkAuth = null;
}
}, 30000);
return () => {
mounted = false;
if (checkAuth) {
clearInterval(checkAuth);
}
clearTimeout(timeout);
};
});
async function registrarPushSubscription() {
try {
// Verificar se Service Worker está disponível antes de tentar
if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
return;
}
// Solicitar subscription com timeout para evitar travamentos
const subscriptionPromise = solicitarPushSubscription();
const timeoutPromise = new Promise<null>((resolve) => setTimeout(() => resolve(null), 5000));
const subscription = await Promise.race([subscriptionPromise, timeoutPromise]);
if (!subscription) {
// Não logar para evitar spam no console quando VAPID key não está configurada
return;
}
// Converter para formato serializável
const subscriptionData = subscriptionToJSON(subscription);
// Registrar no backend com timeout
const mutationPromise = client.mutation(api.pushNotifications.registrarPushSubscription, {
endpoint: subscriptionData.endpoint,
keys: subscriptionData.keys,
userAgent: navigator.userAgent
});
const timeoutMutationPromise = new Promise<{
sucesso: false;
erro: string;
}>((resolve) => setTimeout(() => resolve({ sucesso: false, erro: 'Timeout' }), 5000));
const resultado = await Promise.race([mutationPromise, timeoutMutationPromise]);
if (resultado.sucesso) {
console.log('✅ Push subscription registrada com sucesso');
} else if (resultado.erro && !resultado.erro.includes('Timeout')) {
console.error('❌ Erro ao registrar push subscription:', resultado.erro);
}
} catch (error) {
// Ignorar erros relacionados a message channel fechado
if (error instanceof Error && error.message.includes('message channel')) {
return;
}
console.error('❌ Erro ao configurar push notifications:', error);
}
}
// Remover subscription ao fazer logout
$effect(() => {
if (!currentUser?.data) {
removerPushSubscription().then(() => {
console.log('Push subscription removida');
});
}
});
</script>
<!-- Componente invisível - apenas lógica -->