Skip to content

feat: implementação full-stack de chat realtime com Socket.IO e Clean…#191

Open
glauccoeng-prog wants to merge 1 commit intomate-academy:masterfrom
glauccoeng-prog:develop
Open

feat: implementação full-stack de chat realtime com Socket.IO e Clean…#191
glauccoeng-prog wants to merge 1 commit intomate-academy:masterfrom
glauccoeng-prog:develop

Conversation

@glauccoeng-prog
Copy link
Copy Markdown

… Architecture

Copilot AI review requested due to automatic review settings March 27, 2026 02:12
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implementa a camada de front-end do chat realtime (UI + handlers Socket.IO) com módulos ES para estado, salas, mensagens, modal de rename e comportamento mobile, seguindo a proposta de “Clean Architecture” no cliente.

Changes:

  • Adiciona módulos JS (state/app/rooms/messages/ui/modal/mobile) para orquestração do chat e integração com eventos Socket.IO.
  • Cria a página HTML do chat (username flow, sidebar de salas, modal) e o design system em CSS (tema glassmorphism + responsivo).
  • Implementa renderização segura de mensagens com escape de HTML e auto-scroll.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
public/js/ui.js Renderização de mensagens, empty state, scroll e escape anti-XSS.
public/js/state.js Estado global do cliente + inicialização do socket via io().
public/js/rooms.js Renderização/listeners de salas e ações (join/create/delete/rename modal).
public/js/modal.js Modal de renomear sala + atalhos (Enter/Escape).
public/js/mobile.js Toggle da sidebar/overlay em mobile.
public/js/messages.js Handlers de histórico/mensagens realtime + envio via form + online count.
public/js/app.js Orquestração, fluxo de username, auto-login e reconexão.
public/index.html Estrutura completa da UI + carregamento do Socket.IO client e app module.
public/css/styles.css Estilos completos (layout, componentes, responsividade, modal).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +125 to +126
<!-- Socket.IO Client (carregado do servidor como script global) -->
<script src="/socket.io/socket.io.js"></script>
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O cliente depende do endpoint "/socket.io/socket.io.js" e do global io(), mas o repositório não contém dependência/configuração de Socket.IO (ex.: socket.io/servidor Express) — desse jeito o script vai 404 e o app não inicializa. Inclua a camada de servidor que expõe esse endpoint (ou altere para importar/bundlar socket.io-client).

Suggested change
<!-- Socket.IO Client (carregado do servidor como script global) -->
<script src="/socket.io/socket.io.js"></script>
<!-- Socket.IO Client (carregado de CDN como script global) -->
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js" integrity="sha384-3qG3kzFj1gRG5fiBSzZndKyIsmUZSuO5c12QNbq3cQ83r9E2DyPp4uM9bLaWG+gC" crossorigin="anonymous"></script>

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +76
socket.on('connect', () => {
if (state.username && !usernameScreen.style.display) {
joinWithUsername(state.username);

if (state.currentRoomId) {
socket.emit('room:join', { roomId: state.currentRoomId });
}
}
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A condição de reconexão está invertida: após login você define usernameScreen.style.display = 'none', então !usernameScreen.style.display vira false e o bloco não roda em reconexões. Troque para uma checagem explícita (ex.: usernameScreen.style.display === 'none' ou chatApp.classList.contains('active')) para que o re-join aconteça de fato.

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +114
// Se ainda não está em nenhuma sala, entra na padrão
if (!state.currentRoomId && state.defaultRoomId) {
state.currentRoomId = state.defaultRoomId;

const defaultRoom = rooms.find((r) => r.id === state.defaultRoomId);

if (defaultRoom) {
currentRoomName.textContent = defaultRoom.name;
}
}
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O comentário diz que o cliente "entra na padrão", mas aqui você só atualiza state.currentRoomId/header e não emite room:join. Se o servidor não fizer auto-join, o usuário ficará sem histórico/mensagens até clicar em uma sala; emita socket.emit('room:join', { roomId: state.defaultRoomId }) nesse fluxo (ou documente/garanta o auto-join no backend).

Copilot uses AI. Check for mistakes.
Comment on lines +216 to +219
.room-item:hover .room-actions {
opacity: 1;
}

Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As ações de sala ficam com opacity: 0 e só aparecem em :hover. Em dispositivos touch (sem hover) isso tende a deixar renomear/excluir inacessível/invisível. Considere deixar as ações sempre visíveis em mobile (media query) ou exibir em :focus-within/ao selecionar a sala.

Suggested change
.room-item:hover .room-actions {
opacity: 1;
}
.room-item:hover .room-actions,
.room-item:focus-within .room-actions,
.room-item.active .room-actions {
opacity: 1;
}
@media (max-width: 768px) {
.room-actions {
opacity: 1;
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +76
const renameBtn = document.createElement('button');

renameBtn.textContent = '✏️';
renameBtn.title = 'Renomear';

renameBtn.addEventListener('click', (e) => {
e.stopPropagation();
openRenameModal(room.id, room.name);
});

const deleteBtn = document.createElement('button');

deleteBtn.textContent = '🗑️';
deleteBtn.title = 'Excluir';
deleteBtn.className = 'delete';

deleteBtn.addEventListener('click', (e) => {
e.stopPropagation();
socket.emit('room:delete', { roomId: room.id });
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Os botões de renomear/excluir são apenas emojis e não têm aria-label (nem type="button"). Isso prejudica leitores de tela e pode causar comportamento inesperado se algum dia esses botões forem colocados dentro de um <form>. Adicione aria-label descritivo (e defina type="button").

Copilot uses AI. Check for mistakes.
id="mobileMenuBtn"
class="btn-icon mobile-menu-btn"
style="display: none;"
type="button"
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O botão do menu mobile é apenas um ícone (☰) e não tem nome acessível. Adicione um aria-label (ex.: "Abrir menu de salas") para melhorar acessibilidade por leitor de tela/controle por voz.

Suggested change
type="button"
type="button"
aria-label="Abrir menu de salas"

Copilot uses AI. Check for mistakes.
autocomplete="off"
maxlength="30"
>
<button type="submit" class="btn btn-primary btn-sm">+</button>
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O botão "+" de criar sala não tem nome acessível (apenas símbolo). Para acessibilidade, adicione aria-label (ex.: "Criar sala") ou texto visível.

Suggested change
<button type="submit" class="btn btn-primary btn-sm">+</button>
<button type="submit" class="btn btn-primary btn-sm" aria-label="Criar sala">+</button>

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants