refactor: optimize login attempt logging in Sidebar component to avoid timeouts by deferring user retrieval; enhance MessageList component with improved message processing and state management to prevent unnecessary re-renders
This commit is contained in:
@@ -193,11 +193,10 @@
|
|||||||
if (result.data) {
|
if (result.data) {
|
||||||
// Registrar tentativa de login bem-sucedida
|
// Registrar tentativa de login bem-sucedida
|
||||||
// Fazer de forma assíncrona para não bloquear o login
|
// Fazer de forma assíncrona para não bloquear o login
|
||||||
|
// Não tentamos buscar getCurrentUser aqui porque pode causar timeout
|
||||||
|
// O useQuery no componente já busca o usuário automaticamente quando a sessão estiver pronta
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
// Aguardar um pouco para o usuário ser sincronizado no Convex
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
||||||
|
|
||||||
// Tentar obter GPS se já estiver disponível (não esperar)
|
// Tentar obter GPS se já estiver disponível (não esperar)
|
||||||
let localizacaoGPS: any = {};
|
let localizacaoGPS: any = {};
|
||||||
try {
|
try {
|
||||||
@@ -209,40 +208,21 @@
|
|||||||
// Ignorar se GPS não estiver pronto
|
// Ignorar se GPS não estiver pronto
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buscar o usuário no Convex usando getCurrentUser
|
// Registrar log sem usuarioId - será atualizado depois quando o usuário estiver disponível
|
||||||
const usuario = await convexClient.query(api.auth.getCurrentUser, {});
|
// Isso evita timeouts que ocorrem quando tentamos buscar getCurrentUser imediatamente após login
|
||||||
|
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
|
||||||
if (usuario && usuario._id) {
|
matriculaOuEmail: matricula.trim(),
|
||||||
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
|
sucesso: true,
|
||||||
usuarioId: usuario._id,
|
userAgent: userAgent,
|
||||||
matriculaOuEmail: matricula.trim(),
|
ipAddress: ipPublico,
|
||||||
sucesso: true,
|
latitudeGPS: localizacaoGPS.latitude,
|
||||||
userAgent: userAgent,
|
longitudeGPS: localizacaoGPS.longitude,
|
||||||
ipAddress: ipPublico,
|
precisaoGPS: localizacaoGPS.precisao,
|
||||||
latitudeGPS: localizacaoGPS.latitude,
|
enderecoGPS: localizacaoGPS.endereco,
|
||||||
longitudeGPS: localizacaoGPS.longitude,
|
cidadeGPS: localizacaoGPS.cidade,
|
||||||
precisaoGPS: localizacaoGPS.precisao,
|
estadoGPS: localizacaoGPS.estado,
|
||||||
enderecoGPS: localizacaoGPS.endereco,
|
paisGPS: localizacaoGPS.pais
|
||||||
cidadeGPS: localizacaoGPS.cidade,
|
});
|
||||||
estadoGPS: localizacaoGPS.estado,
|
|
||||||
paisGPS: localizacaoGPS.pais
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Se não encontrou o usuário, registrar sem usuarioId (será atualizado depois)
|
|
||||||
await convexClient.mutation(api.logsLogin.registrarTentativaLogin, {
|
|
||||||
matriculaOuEmail: matricula.trim(),
|
|
||||||
sucesso: true,
|
|
||||||
userAgent: userAgent,
|
|
||||||
ipAddress: ipPublico,
|
|
||||||
latitudeGPS: localizacaoGPS.latitude,
|
|
||||||
longitudeGPS: localizacaoGPS.longitude,
|
|
||||||
precisaoGPS: localizacaoGPS.precisao,
|
|
||||||
enderecoGPS: localizacaoGPS.endereco,
|
|
||||||
cidadeGPS: localizacaoGPS.cidade,
|
|
||||||
estadoGPS: localizacaoGPS.estado,
|
|
||||||
paisGPS: localizacaoGPS.pais
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Erro ao registrar tentativa de login:', err);
|
console.error('Erro ao registrar tentativa de login:', err);
|
||||||
// Não bloquear o login se houver erro ao registrar
|
// Não bloquear o login se houver erro ao registrar
|
||||||
|
|||||||
@@ -38,48 +38,63 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Atualizar lista de mensagens quando a query mudar
|
// Atualizar lista de mensagens quando a query mudar
|
||||||
|
// Usar untrack para evitar loops infinitos
|
||||||
|
let processandoMensagens = false;
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
if (processandoMensagens) return;
|
||||||
if (!mensagensQuery?.data) return;
|
if (!mensagensQuery?.data) return;
|
||||||
|
|
||||||
const resultado = mensagensQuery.data as { mensagens: any[]; hasMore: boolean; nextCursor: Id<'mensagens'> | null };
|
const resultado = mensagensQuery.data as { mensagens: any[]; hasMore: boolean; nextCursor: Id<'mensagens'> | null };
|
||||||
const novasMensagens = resultado.mensagens || [];
|
const novasMensagens = resultado.mensagens || [];
|
||||||
|
|
||||||
// Evitar atualizações desnecessárias comparando IDs das mensagens
|
// Comparação simples usando JSON para evitar loops
|
||||||
if (cursor === null) {
|
const idsAtuais = todasMensagens.map(m => String(m?._id)).sort().join(',');
|
||||||
// Primeira carga: verificar se realmente mudou
|
const idsNovos = novasMensagens.map(m => String(m?._id)).sort().join(',');
|
||||||
const idsAtuais = todasMensagens.map(m => m?._id).filter(Boolean);
|
|
||||||
const idsNovos = novasMensagens.map(m => m?._id).filter(Boolean);
|
|
||||||
const idsIguais = idsAtuais.length === idsNovos.length &&
|
|
||||||
idsAtuais.every((id, i) => id === idsNovos[i]);
|
|
||||||
|
|
||||||
if (!idsIguais) {
|
// Só atualizar se realmente mudou
|
||||||
todasMensagens = novasMensagens;
|
if (idsAtuais !== idsNovos) {
|
||||||
|
processandoMensagens = true;
|
||||||
|
try {
|
||||||
|
if (cursor === null) {
|
||||||
|
todasMensagens = novasMensagens;
|
||||||
|
} else {
|
||||||
|
// Carregamento adicional: adicionar no início
|
||||||
|
const idsExistentes = new Set(todasMensagens.map(m => String(m?._id)));
|
||||||
|
const novasParaAdicionar = novasMensagens.filter(m => m?._id && !idsExistentes.has(String(m._id)));
|
||||||
|
if (novasParaAdicionar.length > 0) {
|
||||||
|
todasMensagens = [...novasParaAdicionar, ...todasMensagens];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hasMore = resultado.hasMore || false;
|
||||||
|
carregandoMais = false;
|
||||||
|
} finally {
|
||||||
|
processandoMensagens = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Carregamento adicional: adicionar no início (mensagens mais antigas)
|
hasMore = resultado.hasMore || false;
|
||||||
// Verificar se já não foram adicionadas
|
carregandoMais = false;
|
||||||
const idsExistentes = new Set(todasMensagens.map(m => m?._id).filter(Boolean));
|
|
||||||
const novasParaAdicionar = novasMensagens.filter(m => m?._id && !idsExistentes.has(m._id));
|
|
||||||
|
|
||||||
if (novasParaAdicionar.length > 0) {
|
|
||||||
todasMensagens = [...novasParaAdicionar, ...todasMensagens];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMore = resultado.hasMore || false;
|
|
||||||
carregandoMais = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Resetar quando mudar de conversa
|
// Resetar quando mudar de conversa
|
||||||
let conversaIdAnterior = $state<string | null>(null);
|
let conversaIdAnterior = $state<string | null>(null);
|
||||||
|
let resetandoConversa = false;
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
if (resetandoConversa) return;
|
||||||
|
|
||||||
const conversaIdAtual = String(conversaId);
|
const conversaIdAtual = String(conversaId);
|
||||||
if (conversaIdAnterior !== null && conversaIdAnterior !== conversaIdAtual) {
|
if (conversaIdAnterior !== null && conversaIdAnterior !== conversaIdAtual) {
|
||||||
cursor = null;
|
resetandoConversa = true;
|
||||||
todasMensagens = [];
|
try {
|
||||||
hasMore = true;
|
cursor = null;
|
||||||
carregandoMais = false;
|
todasMensagens = [];
|
||||||
mensagensComConteudo = [];
|
hasMore = true;
|
||||||
|
carregandoMais = false;
|
||||||
|
mensagensComConteudo = [];
|
||||||
|
ultimoProcessamento = '';
|
||||||
|
} finally {
|
||||||
|
resetandoConversa = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
conversaIdAnterior = conversaIdAtual;
|
conversaIdAnterior = conversaIdAtual;
|
||||||
});
|
});
|
||||||
@@ -542,7 +557,10 @@
|
|||||||
|
|
||||||
// Processar mensagens para descriptografar as criptografadas
|
// Processar mensagens para descriptografar as criptografadas
|
||||||
let ultimoProcessamento = $state<string>('');
|
let ultimoProcessamento = $state<string>('');
|
||||||
|
let processandoDescriptografia = false;
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
if (processandoDescriptografia) return;
|
||||||
|
|
||||||
const mensagens = todasMensagens;
|
const mensagens = todasMensagens;
|
||||||
|
|
||||||
// Se não há mensagens, limpar e retornar
|
// Se não há mensagens, limpar e retornar
|
||||||
@@ -554,13 +572,14 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Criar hash das mensagens para evitar reprocessamento desnecessário
|
// Criar hash simples das mensagens para evitar reprocessamento
|
||||||
const hashMensagens = mensagens.map(m => `${m._id}-${m.criptografado ? '1' : '0'}`).join('|');
|
const hashMensagens = mensagens.map(m => `${String(m._id)}-${m.criptografado ? '1' : '0'}`).join('|');
|
||||||
if (hashMensagens === ultimoProcessamento) {
|
if (hashMensagens === ultimoProcessamento && mensagensComConteudo.length === mensagens.length) {
|
||||||
return; // Já foi processado
|
return; // Já foi processado
|
||||||
}
|
}
|
||||||
|
|
||||||
ultimoProcessamento = hashMensagens;
|
ultimoProcessamento = hashMensagens;
|
||||||
|
processandoDescriptografia = true;
|
||||||
|
|
||||||
const processarMensagens = async () => {
|
const processarMensagens = async () => {
|
||||||
const processadas: Mensagem[] = [];
|
const processadas: Mensagem[] = [];
|
||||||
@@ -638,6 +657,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
mensagensComConteudo = processadas;
|
mensagensComConteudo = processadas;
|
||||||
|
processandoDescriptografia = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
processarMensagens().catch((error) => {
|
processarMensagens().catch((error) => {
|
||||||
@@ -648,6 +668,7 @@
|
|||||||
conteudoDescriptografado: msg.criptografado ? '🔒 Erro ao descriptografar' : msg.conteudo || '',
|
conteudoDescriptografado: msg.criptografado ? '🔒 Erro ao descriptografar' : msg.conteudo || '',
|
||||||
arquivoUrlDescriptografado: msg.criptografado ? null : msg.arquivoUrl || null
|
arquivoUrlDescriptografado: msg.criptografado ? null : msg.arquivoUrl || null
|
||||||
}));
|
}));
|
||||||
|
processandoDescriptografia = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -826,7 +847,7 @@
|
|||||||
bind:this={messagesContainer}
|
bind:this={messagesContainer}
|
||||||
onscroll={handleScroll}
|
onscroll={handleScroll}
|
||||||
>
|
>
|
||||||
{#if todasMensagens.length > 0 || mensagensComConteudo.length > 0}
|
{#if todasMensagens.length > 0}
|
||||||
{@const mensagensParaExibir = mensagensComConteudo.length > 0 ? mensagensComConteudo : todasMensagens}
|
{@const mensagensParaExibir = mensagensComConteudo.length > 0 ? mensagensComConteudo : todasMensagens}
|
||||||
{@const gruposPorDia = agruparMensagensPorDia(mensagensParaExibir)}
|
{@const gruposPorDia = agruparMensagensPorDia(mensagensParaExibir)}
|
||||||
{#each Object.entries(gruposPorDia) as [dia, mensagensDia]}
|
{#each Object.entries(gruposPorDia) as [dia, mensagensDia]}
|
||||||
|
|||||||
@@ -40,32 +40,39 @@ export const createAuth = (
|
|||||||
export const getCurrentUser = query({
|
export const getCurrentUser = query({
|
||||||
args: {},
|
args: {},
|
||||||
handler: async (ctx) => {
|
handler: async (ctx) => {
|
||||||
const authUser = await authComponent.safeGetAuthUser(ctx);
|
try {
|
||||||
if (!authUser) {
|
const authUser = await authComponent.safeGetAuthUser(ctx);
|
||||||
|
if (!authUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await ctx.db
|
||||||
|
.query('usuarios')
|
||||||
|
.withIndex('authId', (q) => q.eq('authId', authUser._id))
|
||||||
|
.unique();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buscar foto de perfil e role em paralelo para melhor performance
|
||||||
|
const [fotoPerfilUrl, role] = await Promise.all([
|
||||||
|
user.fotoPerfil ? ctx.storage.getUrl(user.fotoPerfil).catch(() => null) : Promise.resolve(null),
|
||||||
|
user.roleId
|
||||||
|
? ctx.db
|
||||||
|
.query('roles')
|
||||||
|
.withIndex('by_id', (q) => q.eq('_id', user.roleId))
|
||||||
|
.unique()
|
||||||
|
.catch(() => null)
|
||||||
|
: Promise.resolve(null)
|
||||||
|
]);
|
||||||
|
|
||||||
|
return { ...user, role: role || null, fotoPerfilUrl };
|
||||||
|
} catch (error) {
|
||||||
|
// Log do erro mas não falhar completamente - retornar null para permitir retry
|
||||||
|
console.error('Erro ao buscar usuário atual:', error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await ctx.db
|
|
||||||
.query('usuarios')
|
|
||||||
.withIndex('authId', (q) => q.eq('authId', authUser._id))
|
|
||||||
.unique();
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fotoPerfilUrl = user.fotoPerfil ? await ctx.storage.getUrl(user.fotoPerfil) : null;
|
|
||||||
|
|
||||||
if (!user.roleId) {
|
|
||||||
return { ...user, role: null, fotoPerfilUrl };
|
|
||||||
}
|
|
||||||
|
|
||||||
const role = await ctx.db
|
|
||||||
.query('roles')
|
|
||||||
.withIndex('by_id', (q) => q.eq('_id', user.roleId))
|
|
||||||
.unique();
|
|
||||||
|
|
||||||
return { ...user, role, fotoPerfilUrl };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user