diff --git a/apps/web/src/lib/components/PushNotificationManager.svelte b/apps/web/src/lib/components/PushNotificationManager.svelte index c1b47f9..be11326 100644 --- a/apps/web/src/lib/components/PushNotificationManager.svelte +++ b/apps/web/src/lib/components/PushNotificationManager.svelte @@ -12,51 +12,116 @@ const client = useConvexClient(); + // 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 | null = null; + let mounted = true; + // Aguardar usuário estar autenticado - const checkAuth = setInterval(async () => { - if (authStore.usuario) { - clearInterval(checkAuth); - await registrarPushSubscription(); + checkAuth = setInterval(async () => { + if (authStore.usuario && 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) - setTimeout(() => { - clearInterval(checkAuth); + const timeout = setTimeout(() => { + if (checkAuth) { + clearInterval(checkAuth); + checkAuth = null; + } }, 30000); return () => { - clearInterval(checkAuth); + mounted = false; + if (checkAuth) { + clearInterval(checkAuth); + } + clearTimeout(timeout); }; }); async function registrarPushSubscription() { try { - // Solicitar subscription - const subscription = await solicitarPushSubscription(); + // 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((resolve) => + setTimeout(() => resolve(null), 5000) + ); + + const subscription = await Promise.race([subscriptionPromise, timeoutPromise]); if (!subscription) { - console.log("ℹ️ Push subscription não disponível ou permissão negada"); + // 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 - const resultado = await client.mutation(api.pushNotifications.registrarPushSubscription, { + // 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 { + } 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); } } diff --git a/apps/web/src/lib/components/Sidebar.svelte b/apps/web/src/lib/components/Sidebar.svelte index 7ef45b5..6c0fc37 100644 --- a/apps/web/src/lib/components/Sidebar.svelte +++ b/apps/web/src/lib/components/Sidebar.svelte @@ -11,6 +11,7 @@ import ChatWidget from "$lib/components/chat/ChatWidget.svelte"; import PresenceManager from "$lib/components/chat/PresenceManager.svelte"; import { getBrowserInfo } from "$lib/utils/browserInfo"; + import { getAvatarUrl } from "$lib/utils/avatarGenerator"; let { children }: { children: Snippet } = $props(); @@ -19,6 +20,22 @@ // Caminho atual da página const currentPath = $derived(page.url.pathname); + // Função para obter a URL do avatar/foto do usuário + const avatarUrlDoUsuario = $derived(() => { + const usuario = authStore.usuario; + if (!usuario) return null; + + // Prioridade: fotoPerfilUrl > avatar > fallback com nome + if (usuario.fotoPerfilUrl) { + return usuario.fotoPerfilUrl; + } + if (usuario.avatar) { + return getAvatarUrl(usuario.avatar); + } + // Fallback: gerar avatar baseado no nome + return getAvatarUrl(usuario.nome); + }); + // Função para gerar classes do menu ativo function getMenuClasses(isActive: boolean) { const baseClasses = "group font-semibold flex items-center justify-center gap-2 text-center p-3.5 rounded-xl border-2 transition-all duration-300 shadow-md hover:shadow-lg hover:scale-105"; @@ -209,12 +226,14 @@

-
+
{#if authStore.autenticado} - - + +
+ +
-