feat: enhance ErrorModal and chat components with new features and improvements
- Refactored ErrorModal to utilize a dialog element for better accessibility and user experience, including a close button with an icon. - Updated chat components to improve participant display and message read status, enhancing user engagement and clarity. - Introduced loading indicators for user and conversation data in SalaReuniaoManager to improve responsiveness during data fetching. - Enhanced message handling in MessageList to indicate whether messages have been read, providing users with better feedback on message status. - Improved overall structure and styling across various components for consistency and maintainability.
This commit is contained in:
@@ -346,6 +346,7 @@ export const enviarMensagem = mutation({
|
||||
mencoes: args.mencoes,
|
||||
respostaPara: args.respostaPara,
|
||||
enviadaEm: Date.now(),
|
||||
lidaPor: [], // Inicializar como array vazio
|
||||
});
|
||||
|
||||
// Detectar URLs no conteúdo e extrair preview (apenas para mensagens de texto, assíncrono)
|
||||
@@ -495,14 +496,19 @@ export const agendarMensagem = mutation({
|
||||
throw new Error("Você não pertence a esta conversa");
|
||||
}
|
||||
|
||||
// Normalizar conteúdo para busca
|
||||
const conteudoBusca = normalizarTextoParaBusca(args.conteudo);
|
||||
|
||||
// Criar mensagem agendada
|
||||
const mensagemId = await ctx.db.insert("mensagens", {
|
||||
conversaId: args.conversaId,
|
||||
remetenteId: usuarioAtual._id,
|
||||
tipo: "texto",
|
||||
conteudo: args.conteudo,
|
||||
conteudoBusca,
|
||||
agendadaPara: args.agendadaPara,
|
||||
enviadaEm: args.agendadaPara, // Será usada quando a mensagem for enviada
|
||||
enviadaEm: args.agendadaPara, // Será atualizado quando a mensagem for enviada
|
||||
lidaPor: [], // Inicializar como array vazio
|
||||
});
|
||||
|
||||
return mensagemId;
|
||||
@@ -662,6 +668,29 @@ export const marcarComoLida = mutation({
|
||||
});
|
||||
}
|
||||
|
||||
// Atualizar status de leitura nas mensagens
|
||||
// Buscar todas as mensagens até a mensagem atual (incluindo ela) na conversa
|
||||
const todasMensagens = await ctx.db
|
||||
.query("mensagens")
|
||||
.withIndex("by_conversa", (q) => q.eq("conversaId", args.conversaId))
|
||||
.filter((q) =>
|
||||
q.and(
|
||||
q.lte(q.field("enviadaEm"), mensagem.enviadaEm),
|
||||
q.neq(q.field("remetenteId"), usuarioAtual._id) // Apenas mensagens de outros usuários
|
||||
)
|
||||
)
|
||||
.collect();
|
||||
|
||||
// Atualizar cada mensagem para incluir o usuário atual no array lidaPor (se ainda não estiver)
|
||||
for (const msg of todasMensagens) {
|
||||
const lidaPor = msg.lidaPor || [];
|
||||
if (!lidaPor.includes(usuarioAtual._id)) {
|
||||
await ctx.db.patch(msg._id, {
|
||||
lidaPor: [...lidaPor, usuarioAtual._id],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Marcar notificações desta conversa como lidas
|
||||
const notificacoes = await ctx.db
|
||||
.query("notificacoes")
|
||||
@@ -1455,16 +1484,33 @@ export const enviarNotificacaoReuniao = mutation({
|
||||
|
||||
// Criar notificação para todos os participantes
|
||||
for (const participanteId of conversa.participantes) {
|
||||
const tituloNotificacao = args.titulo || "Notificação da sala de reunião";
|
||||
const descricaoNotificacao = args.mensagem.substring(0, 100); // Limitar descrição para push
|
||||
|
||||
// Criar notificação no banco
|
||||
await ctx.db.insert("notificacoes", {
|
||||
usuarioId: participanteId,
|
||||
tipo: "nova_mensagem",
|
||||
conversaId: args.conversaId,
|
||||
remetenteId: usuarioAtual._id,
|
||||
titulo: args.titulo || "Notificação da sala de reunião",
|
||||
titulo: tituloNotificacao,
|
||||
descricao: args.mensagem,
|
||||
lida: false,
|
||||
criadaEm: Date.now(),
|
||||
});
|
||||
|
||||
// Enviar push notification (assíncrono, não bloqueia)
|
||||
ctx.scheduler.runAfter(0, internal.pushNotifications.enviarPushNotification, {
|
||||
usuarioId: participanteId,
|
||||
titulo: tituloNotificacao,
|
||||
corpo: descricaoNotificacao,
|
||||
data: {
|
||||
conversaId: args.conversaId,
|
||||
tipo: "notificacao_reuniao",
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.error(`Erro ao agendar push para usuário ${participanteId}:`, error);
|
||||
});
|
||||
}
|
||||
|
||||
return { sucesso: true };
|
||||
@@ -1952,6 +1998,13 @@ export const listarTodosUsuarios = query({
|
||||
const funcionario = await ctx.db.get(u.funcionarioId);
|
||||
matricula = funcionario?.matricula;
|
||||
}
|
||||
|
||||
// Buscar URL da foto de perfil se existir
|
||||
let fotoPerfilUrl: string | null = null;
|
||||
if (u.fotoPerfil) {
|
||||
fotoPerfilUrl = await ctx.storage.getUrl(u.fotoPerfil);
|
||||
}
|
||||
|
||||
return {
|
||||
_id: u._id,
|
||||
nome: u.nome,
|
||||
@@ -1959,6 +2012,7 @@ export const listarTodosUsuarios = query({
|
||||
matricula,
|
||||
avatar: u.avatar,
|
||||
fotoPerfil: u.fotoPerfil,
|
||||
fotoPerfilUrl,
|
||||
statusPresenca: u.statusPresenca,
|
||||
statusMensagem: u.statusMensagem,
|
||||
setor: u.setor,
|
||||
@@ -2246,22 +2300,23 @@ export const enviarMensagensAgendadas = internalMutation({
|
||||
const agora = Date.now();
|
||||
|
||||
// Buscar mensagens que deveriam ser enviadas
|
||||
// Como o índice by_agendamento indexa por agendadaPara, podemos usar range query
|
||||
// Buscar mensagens com agendadaPara entre 0 e agora (mensagens agendadas que já devem ser enviadas)
|
||||
// Valores undefined não aparecem no índice, então só buscamos mensagens realmente agendadas
|
||||
const mensagensAgendadas = await ctx.db
|
||||
.query("mensagens")
|
||||
.withIndex("by_agendamento")
|
||||
.filter((q) =>
|
||||
q.and(
|
||||
q.neq(q.field("agendadaPara"), undefined),
|
||||
q.lte(q.field("agendadaPara"), agora)
|
||||
)
|
||||
)
|
||||
.withIndex("by_agendamento", (q) => q.gte("agendadaPara", 0).lte("agendadaPara", agora))
|
||||
.collect();
|
||||
|
||||
for (const mensagem of mensagensAgendadas) {
|
||||
// Normalizar conteúdo para busca (se ainda não foi feito)
|
||||
const conteudoBusca = mensagem.conteudoBusca || normalizarTextoParaBusca(mensagem.conteudo);
|
||||
|
||||
// Atualizar mensagem para "enviada"
|
||||
await ctx.db.patch(mensagem._id, {
|
||||
agendadaPara: undefined,
|
||||
enviadaEm: agora,
|
||||
conteudoBusca: conteudoBusca, // Garantir que tem conteúdo de busca
|
||||
});
|
||||
|
||||
// Atualizar última mensagem da conversa
|
||||
@@ -2275,19 +2330,43 @@ export const enviarMensagensAgendadas = internalMutation({
|
||||
|
||||
// Criar notificações para outros participantes
|
||||
const remetente = await ctx.db.get(mensagem.remetenteId);
|
||||
for (const participanteId of conversa.participantes) {
|
||||
if (participanteId !== mensagem.remetenteId) {
|
||||
await ctx.db.insert("notificacoes", {
|
||||
usuarioId: participanteId,
|
||||
tipo: "nova_mensagem",
|
||||
conversaId: mensagem.conversaId,
|
||||
mensagemId: mensagem._id,
|
||||
remetenteId: mensagem.remetenteId,
|
||||
titulo: `Nova mensagem de ${remetente?.nome || "Usuário"}`,
|
||||
descricao: mensagem.conteudo.substring(0, 100),
|
||||
lida: false,
|
||||
criadaEm: agora,
|
||||
});
|
||||
if (remetente) {
|
||||
// Determinar tipo de notificação (se há menções)
|
||||
const tipoNotificacao = mensagem.mencoes && mensagem.mencoes.length > 0 ? "mencao" : "nova_mensagem";
|
||||
const titulo = tipoNotificacao === "mencao"
|
||||
? `${remetente.nome} mencionou você`
|
||||
: `Nova mensagem de ${remetente.nome}`;
|
||||
const descricao = mensagem.conteudo.substring(0, 100);
|
||||
|
||||
for (const participanteId of conversa.participantes) {
|
||||
if (participanteId !== mensagem.remetenteId) {
|
||||
// Criar notificação no banco
|
||||
await ctx.db.insert("notificacoes", {
|
||||
usuarioId: participanteId,
|
||||
tipo: tipoNotificacao,
|
||||
conversaId: mensagem.conversaId,
|
||||
mensagemId: mensagem._id,
|
||||
remetenteId: mensagem.remetenteId,
|
||||
titulo,
|
||||
descricao,
|
||||
lida: false,
|
||||
criadaEm: agora,
|
||||
});
|
||||
|
||||
// Enviar push notification (assíncrono, não bloqueia)
|
||||
ctx.scheduler.runAfter(0, internal.pushNotifications.enviarPushNotification, {
|
||||
usuarioId: participanteId,
|
||||
titulo,
|
||||
corpo: descricao,
|
||||
data: {
|
||||
conversaId: mensagem.conversaId,
|
||||
mensagemId: mensagem._id,
|
||||
tipo: tipoNotificacao,
|
||||
},
|
||||
}).catch((error) => {
|
||||
console.error(`Erro ao agendar push para usuário ${participanteId}:`, error);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,6 +671,7 @@ export default defineSchema({
|
||||
enviadaEm: v.number(),
|
||||
editadaEm: v.optional(v.number()),
|
||||
deletada: v.optional(v.boolean()),
|
||||
lidaPor: v.optional(v.array(v.id("usuarios"))), // IDs dos usuários que leram a mensagem
|
||||
})
|
||||
.index("by_conversa", ["conversaId", "enviadaEm"])
|
||||
.index("by_remetente", ["remetenteId"])
|
||||
|
||||
Reference in New Issue
Block a user