feat: enhance chat components with improved accessibility features, including ARIA attributes for search and user status, and implement message length validation and file type checks in message input handling

This commit is contained in:
2025-12-08 23:16:05 -03:00
parent e46738c5bf
commit 1810cbabe2
22 changed files with 1364 additions and 249 deletions

View File

@@ -17,6 +17,47 @@
let heartbeatInterval: ReturnType<typeof setInterval> | null = null;
let inactivityTimeout: ReturnType<typeof setTimeout> | null = null;
let lastActivity = Date.now();
let lastStatusUpdate = 0;
let pendingStatusUpdate: ReturnType<typeof setTimeout> | null = null;
const STATUS_UPDATE_THROTTLE = 5000; // 5 segundos entre atualizações
// Função auxiliar para atualizar status com throttle e tratamento de erro
async function atualizarStatusPresencaSeguro(status: 'online' | 'offline' | 'ausente' | 'externo' | 'em_reuniao') {
if (!usuarioAutenticado) return;
const now = Date.now();
// Throttle: só atualizar se passou tempo suficiente desde a última atualização
if (now - lastStatusUpdate < STATUS_UPDATE_THROTTLE) {
// Cancelar atualização pendente se houver
if (pendingStatusUpdate) {
clearTimeout(pendingStatusUpdate);
}
// Agendar atualização para depois do throttle
pendingStatusUpdate = setTimeout(() => {
atualizarStatusPresencaSeguro(status);
}, STATUS_UPDATE_THROTTLE - (now - lastStatusUpdate));
return;
}
// Limpar atualização pendente se houver
if (pendingStatusUpdate) {
clearTimeout(pendingStatusUpdate);
pendingStatusUpdate = null;
}
lastStatusUpdate = now;
try {
await client.mutation(api.chat.atualizarStatusPresenca, { status });
} catch (error) {
// Silenciar erros de timeout - não são críticos para a funcionalidade
const errorMessage = error instanceof Error ? error.message : String(error);
const isTimeout = errorMessage.includes('timed out') || errorMessage.includes('timeout');
if (!isTimeout) {
console.error('Erro ao atualizar status de presença:', error);
}
}
}
// Detectar atividade do usuário
function handleActivity() {
@@ -33,7 +74,7 @@
inactivityTimeout = setTimeout(
() => {
if (usuarioAutenticado) {
client.mutation(api.chat.atualizarStatusPresenca, { status: 'ausente' });
atualizarStatusPresencaSeguro('ausente');
}
},
5 * 60 * 1000
@@ -45,7 +86,7 @@
if (!usuarioAutenticado) return;
// Configurar como online ao montar (apenas se autenticado)
client.mutation(api.chat.atualizarStatusPresenca, { status: 'online' });
atualizarStatusPresencaSeguro('online');
// Heartbeat a cada 30 segundos (apenas se autenticado)
heartbeatInterval = setInterval(() => {
@@ -61,7 +102,7 @@
// Se houve atividade nos últimos 5 minutos, manter online
if (timeSinceLastActivity < 5 * 60 * 1000) {
client.mutation(api.chat.atualizarStatusPresenca, { status: 'online' });
atualizarStatusPresencaSeguro('online');
}
}, 30 * 1000);
@@ -82,10 +123,10 @@
if (document.hidden) {
// Aba ficou inativa
client.mutation(api.chat.atualizarStatusPresenca, { status: 'ausente' });
atualizarStatusPresencaSeguro('ausente');
} else {
// Aba ficou ativa
client.mutation(api.chat.atualizarStatusPresenca, { status: 'online' });
atualizarStatusPresencaSeguro('online');
handleActivity();
}
}
@@ -94,9 +135,15 @@
// Cleanup
return () => {
// Limpar atualização pendente
if (pendingStatusUpdate) {
clearTimeout(pendingStatusUpdate);
pendingStatusUpdate = null;
}
// Marcar como offline ao desmontar (apenas se autenticado)
if (usuarioAutenticado) {
client.mutation(api.chat.atualizarStatusPresenca, { status: 'offline' });
atualizarStatusPresencaSeguro('offline');
}
if (heartbeatInterval) {