feat(screencast,chat): roteamento read-only, avisos de sessão e TCP privado

SessionNoticeMessage encerra abas de recebimento; abas ↑/↓; chat unifica bucket peer e conexão TCP.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Gabriel Felipe Souza SantosandCursor committed 2026-10-27 18:48:16 -03:00
1 parent fc657345ba
commit 112b6edaeb
14 files changed
+406 -36

No files matched your search

@@ -305,8 +305,8 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
@Override
public void onPrivateChannelReady(String peerIp) {
SwingUtilities.invokeLater(() -> {
appendSystemLine(privateKey(peerIp), "Canal TCP privado conectado com " + peerIp + ".");
if (privateKey(peerIp).equals(activeConversationKey)) {
appendSystemLine(peerKey(canonicalPeerIp(peerIp)), "Canal TCP privado conectado com " + peerIp + ".");
if (peerKey(canonicalPeerIp(peerIp)).equals(activeConversationKey)) {
flushTranscriptToPane();
}
});
@@ -315,8 +315,8 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
@Override
public void onPrivateChannelClosed(String peerIp) {
SwingUtilities.invokeLater(() -> {
appendSystemLine(privateKey(peerIp), "Canal TCP privado encerrado (" + peerIp + ").");
if (privateKey(peerIp).equals(activeConversationKey)) {
appendSystemLine(peerKey(canonicalPeerIp(peerIp)), "Canal TCP privado encerrado (" + peerIp + ").");
if (peerKey(canonicalPeerIp(peerIp)).equals(activeConversationKey)) {
flushTranscriptToPane();
}
});
@@ -324,7 +324,13 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
@Override
public void appendPrivateLine(String peerIp, LegacyMpChatMessage message, boolean outgoing) {
SwingUtilities.invokeLater(() -> appendLegacyLine(privateKey(peerIp), message));
SwingUtilities.invokeLater(() -> {
String canonical = canonicalPeerIp(peerIp);
if (!outgoing) {
ensurePeerTabForIncoming(canonical, message.getNome());
}
appendLegacyLine(peerKey(canonical), message);
});
}
private void buildUi() {
@@ -418,8 +424,8 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
if (privateChatController != null) {
privateChatController.openChat(user);
}
appendSystemLine(privateKey(PeerIpv4.fromInetAddress(user.getAddress())),
"Canal TCP privado conectando… envie de novo em instantes.");
appendSystemLine(peerKey(canonicalPeerIp(PeerIpv4.fromInetAddress(user.getAddress()))),
"Canal TCP privado conectando…");
return;
}
@@ -499,7 +505,7 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
public void receiveLegacyMessage(String tipo, String nome, String ip, String text) {
runOnEdt(() -> {
LegacyMpChatMessage legacy = new LegacyMpChatMessage(nome, ip, tipo, text);
String bucket = ip != null && !ip.isBlank() ? privateKey(canonicalPeerIp(ip)) : ROOM_KEY;
String bucket = ip != null && !ip.isBlank() ? peerKey(canonicalPeerIp(ip)) : ROOM_KEY;
appendLegacyLine(bucket, legacy);
});
}
@@ -551,7 +557,7 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
}
if (key != null && key.startsWith("peer:") && !PluginSessionKeys.PEER_DRAFT.equals(key)) {
String tabIp = canonicalPeerIp(key.substring("peer:".length()));
return peerKey(tabIp).equals(bucket);
return peerKey(tabIp).equals(bucket) || privateKey(tabIp).equals(bucket);
}
return false;
}
@@ -609,9 +615,6 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
if (ip == null || ip.isEmpty()) {
return ROOM_KEY;
}
if (routing.privateTcpOn()) {
return privateKey(ip);
}
return peerKey(ip);
}
@@ -18,6 +18,7 @@ public class PrivateChatController {
private final PrivateChatHost host;
private final Map<String, TcpConnection> connections = new HashMap<>();
private final Set<String> pendingConnections = new HashSet<>();
private final Map<String, PendingSend> pendingTextByAddress = new HashMap<>();
private PrivateChatUiSink uiSink;
public PrivateChatController(ConnectionManager connectionManager, PrivateChatHost host) {
@@ -38,10 +39,9 @@ public class PrivateChatController {
return;
}
String address = PeerIpv4.fromInetAddress(user.getAddress());
if (connections.containsKey(address)) {
if (uiSink != null) {
uiSink.onPrivateChannelReady(address);
}
TcpConnection existing = connections.get(address);
if (existing != null && !existing.isClosed()) {
flushPendingSend(address, user);
return;
}
if (pendingConnections.contains(address)) {
@@ -51,6 +51,17 @@ public class PrivateChatController {
host.openConnection(address);
}
public void registerOutgoingConnection(String address, TcpConnection connection) {
if (address == null || connection == null) {
return;
}
String canonical = PeerIpv4.normalize(address);
pendingConnections.remove(canonical);
replaceConnection(canonical, connection, false);
User user = new User("", connection.getAddress(), System.currentTimeMillis(), null);
flushPendingSend(canonical, user);
}
public void receiveMessage(TcpConnection connection, LegacyMpChatMessage message) {
if (uiSink != null) {
uiSink.appendPrivateLine(addressKey(connection), message, false);
@@ -60,8 +71,17 @@ public class PrivateChatController {
public void connectionReceived(TcpConnection connection) {
String address = addressKey(connection);
pendingConnections.remove(address);
replaceConnection(address, connection, true);
}
private void replaceConnection(String address, TcpConnection connection, boolean notifyReady) {
TcpConnection existing = connections.get(address);
if (existing != null && !existing.isClosed() && existing != connection) {
return;
}
boolean first = existing == null || existing.isClosed();
connections.put(address, connection);
if (uiSink != null) {
if (notifyReady && first && uiSink != null) {
uiSink.onPrivateChannelReady(address);
}
}
@@ -69,6 +89,8 @@ public class PrivateChatController {
public void closeChat(TcpConnection connection) {
String address = addressKey(connection);
connections.remove(address);
pendingConnections.remove(address);
pendingTextByAddress.remove(address);
if (uiSink != null) {
uiSink.onPrivateChannelClosed(address);
}
@@ -80,9 +102,24 @@ public class PrivateChatController {
}
String address = PeerIpv4.fromInetAddress(user.getAddress());
TcpConnection connection = connections.get(address);
if (connection == null) {
if (connection == null || connection.isClosed()) {
if (pendingConnections.contains(address)) {
pendingTextByAddress.put(address, new PendingSend(user, text));
return true;
}
return false;
}
return sendOnConnection(connection, address, text);
}
private void flushPendingSend(String address, User user) {
PendingSend pending = pendingTextByAddress.remove(address);
if (pending != null) {
sendText(user != null ? user : pending.user, pending.text);
}
}
private boolean sendOnConnection(TcpConnection connection, String address, String text) {
try {
LegacyMpChatMessage message = new LegacyMpChatMessage(
connectionManager.getUserName(),
@@ -105,6 +142,10 @@ public class PrivateChatController {
if (user == null || user.getAddress() == null) {
return false;
}
return connections.containsKey(PeerIpv4.fromInetAddress(user.getAddress()));
TcpConnection connection = connections.get(PeerIpv4.fromInetAddress(user.getAddress()));
return connection != null && !connection.isClosed();
}
private record PendingSend(User user, String text) {
}
}
@@ -139,11 +139,12 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
@Override
public void openConnection(String ip) {
try {
connectionManager.connectPluginTcp(
TcpConnection connection = connectionManager.connectPluginTcp(
InetAddress.getByName(ip),
getIdentifier(),
getIdentifier()
);
privateChatController.registerOutgoingConnection(ip, connection);
} catch (Exception e) {
System.out.println("Error opening connection: " + e.getMessage());
}