feat(ui): abas passivas sem badges na lista lateral (v1)
Sub-abas de chat e screencast são criadas em segundo plano ao receber; D23 adiado na UI. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
1414f59a20
commit
fc657345ba
10 files changed
+87
-198
No files matched your search
@@ -20,6 +20,6 @@
|
|||||||
| D20 | Peer actions por plugin no popup | Lista lateral agrupa ações de todos os plugins (submenus), não só a aba ativa | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
| D20 | Peer actions por plugin no popup | Lista lateral agrupa ações de todos os plugins (submenus), não só a aba ativa | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
||||||
| D21 | `onShellTabClosed` | Fechar aba do plugin na shell notifica o plugin (ex.: parar screencast) | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
| D21 | `onShellTabClosed` | Fechar aba do plugin na shell notifica o plugin (ex.: parar screencast) | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
||||||
| D22 | `PeerIpv4` para IP de peer na UI | No Windows, `InetAddress.getHostAddress()` pode devolver IPv6-mapeado e sufixo de zona (`%n`); chat e roteamento usam IPv4 pontilhado | [appendix_portas_e_multicast.md](documentation/appendix_portas_e_multicast.md) |
|
| D22 | `PeerIpv4` para IP de peer na UI | No Windows, `InetAddress.getHostAddress()` pode devolver IPv6-mapeado e sufixo de zona (`%n`); chat e roteamento usam IPv4 pontilhado | [appendix_portas_e_multicast.md](documentation/appendix_portas_e_multicast.md) |
|
||||||
| D23 | Foco de atividade e badges | `PeerActivityFocus` + `PeerActivityFocusTokens`; chat usa contagem (`recordCounted`), screencast usa indicador (`signal`); suprime badge no canal em foco | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
| D23 | Foco de atividade e badges | **Adiado na v1** — núcleo (`PeerActivityFocus` / `PeerActivityInbox`) mantido; UI sem badges; recebimento passivo em sub-abas (`addTabIfAbsent`) | [09_sistema_de_plugins.md](documentation/09_sistema_de_plugins.md) |
|
||||||
|
|
||||||
Novas decisões: adicionar linha nesta tabela e detalhar no arquivo técnico correspondente.
|
Novas decisões: adicionar linha nesta tabela e detalhar no arquivo técnico correspondente.
|
||||||
@@ -48,30 +48,16 @@ public final class PresenceService {
|
|||||||
|
|
||||||
public List<UserPeerView> listPeerViews() {
|
public List<UserPeerView> listPeerViews() {
|
||||||
List<UserPeerView> views = new ArrayList<>();
|
List<UserPeerView> views = new ArrayList<>();
|
||||||
int broadcastCounted = peerActivityInbox != null
|
views.add(new UserPeerView("Todos", null, null, 0, false, false, true));
|
||||||
? peerActivityInbox.totalCountedForPeer(PeerActivityInbox.BROADCAST_PEER_KEY)
|
|
||||||
: 0;
|
|
||||||
boolean broadcastIndicator = peerActivityInbox != null
|
|
||||||
&& peerActivityInbox.hasIndicatorForPeer(PeerActivityInbox.BROADCAST_PEER_KEY);
|
|
||||||
views.add(new UserPeerView("Todos", null, null, broadcastCounted, broadcastIndicator, false, true));
|
|
||||||
String selfId = heartbeatManager.getInstanceId();
|
String selfId = heartbeatManager.getInstanceId();
|
||||||
for (User user : heartbeatManager.getPeers()) {
|
for (User user : heartbeatManager.getPeers()) {
|
||||||
boolean isSelf = selfId.equals(user.getInstanceId());
|
boolean isSelf = selfId.equals(user.getInstanceId());
|
||||||
int notifications = 0;
|
|
||||||
if (inviteInboxService != null && user.getInstanceId() != null) {
|
|
||||||
notifications += inviteInboxService.getNotificationCount(user.getInstanceId());
|
|
||||||
}
|
|
||||||
boolean activityIndicator = false;
|
|
||||||
if (peerActivityInbox != null && user.getInstanceId() != null) {
|
|
||||||
notifications += peerActivityInbox.totalCountedForPeer(user.getInstanceId());
|
|
||||||
activityIndicator = peerActivityInbox.hasIndicatorForPeer(user.getInstanceId());
|
|
||||||
}
|
|
||||||
views.add(new UserPeerView(
|
views.add(new UserPeerView(
|
||||||
user.getUserName(),
|
user.getUserName(),
|
||||||
user.getInstanceId(),
|
user.getInstanceId(),
|
||||||
user.getAddress(),
|
user.getAddress(),
|
||||||
notifications,
|
0,
|
||||||
activityIndicator,
|
false,
|
||||||
isSelf,
|
isSelf,
|
||||||
false
|
false
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -116,6 +116,39 @@ public final class ClosableTabbedPane {
|
|||||||
tabbedPane.setSelectedIndex(newIndex);
|
tabbedPane.setSelectedIndex(newIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addTabIfAbsent(String key, String title, JComponent content, boolean closable) {
|
||||||
|
addTabIfAbsent(key, title, title, content, closable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cria a aba se não existir, sem mudar a aba selecionada.
|
||||||
|
*/
|
||||||
|
public void addTabIfAbsent(String key, String title, String tooltip, JComponent content, boolean closable) {
|
||||||
|
reconcileKeyListWithTabPane();
|
||||||
|
dedupeKey(key);
|
||||||
|
int index = indexOfKey(key);
|
||||||
|
if (index >= 0 && index < tabbedPane.getTabCount()) {
|
||||||
|
setTabHeader(index, title, tooltip, closable, key);
|
||||||
|
reindex();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int selectedBefore = tabbedPane.getSelectedIndex();
|
||||||
|
tabbedPane.addTab(title, null);
|
||||||
|
int newIndex = tabbedPane.getTabCount() - 1;
|
||||||
|
orderedKeys.add(key);
|
||||||
|
setTabHeader(newIndex, title, tooltip, closable, key);
|
||||||
|
reindex();
|
||||||
|
if (selectedBefore >= 0 && selectedBefore < tabbedPane.getTabCount()) {
|
||||||
|
boolean prev = suppressSelectionEvents;
|
||||||
|
suppressSelectionEvents = true;
|
||||||
|
try {
|
||||||
|
tabbedPane.setSelectedIndex(selectedBefore);
|
||||||
|
} finally {
|
||||||
|
suppressSelectionEvents = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void closeTab(String key) {
|
public void closeTab(String key) {
|
||||||
closeTab(key, closeFallbackKey);
|
closeTab(key, closeFallbackKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package pitiupi.presentation.shell;
|
package pitiupi.presentation.shell;
|
||||||
|
|
||||||
import pitiupi.application.activity.PeerActivityInbox;
|
|
||||||
import pitiupi.application.peeractions.GroupedPeerActions;
|
import pitiupi.application.peeractions.GroupedPeerActions;
|
||||||
import pitiupi.application.peeractions.PeerAction;
|
import pitiupi.application.peeractions.PeerAction;
|
||||||
import pitiupi.application.peeractions.PeerActionBroker;
|
import pitiupi.application.peeractions.PeerActionBroker;
|
||||||
@@ -35,7 +34,7 @@ import java.util.Map;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lista lateral com Todos, badges e peer actions.
|
* Lista lateral com Todos e peer actions (sem badges de atividade na v1).
|
||||||
*/
|
*/
|
||||||
public final class NetworkUsersPanel extends JPanel implements PeerListener {
|
public final class NetworkUsersPanel extends JPanel implements PeerListener {
|
||||||
|
|
||||||
@@ -68,11 +67,7 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
|
|||||||
JLabel label = (JLabel) super.getListCellRendererComponent(
|
JLabel label = (JLabel) super.getListCellRendererComponent(
|
||||||
list, value, index, isSelected, cellHasFocus);
|
list, value, index, isSelected, cellHasFocus);
|
||||||
if (value instanceof DisplayPeer peer) {
|
if (value instanceof DisplayPeer peer) {
|
||||||
label.setText(ShellUi.formatPeerListLabel(
|
label.setText(peer.view.getDisplayName());
|
||||||
peer.view.getDisplayName(),
|
|
||||||
peer.view.getNotificationCount(),
|
|
||||||
peer.view.hasActivityIndicator()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
@@ -98,12 +93,6 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
|
|||||||
|
|
||||||
connectionManager.addPeerListener(this);
|
connectionManager.addPeerListener(this);
|
||||||
refreshFromPresence();
|
refreshFromPresence();
|
||||||
connectionManager.getInviteInboxService().setOnNotificationsChanged(this::refreshFromPresence);
|
|
||||||
connectionManager.getPeerActivityInbox().setOnChanged(this::refreshFromPresence);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isIndicatorAction(String actionId) {
|
|
||||||
return actionId != null && actionId.startsWith("screencast.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showPeerActions(DisplayPeer peer, PeerActionTarget target, int x, int y) {
|
private void showPeerActions(DisplayPeer peer, PeerActionTarget target, int x, int y) {
|
||||||
@@ -113,27 +102,11 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
|
|||||||
? new PeerTarget.All()
|
? new PeerTarget.All()
|
||||||
: new PeerTarget.Single(peer.view);
|
: new PeerTarget.Single(peer.view);
|
||||||
|
|
||||||
PeerActivityInbox activity = connectionManager.getPeerActivityInbox();
|
|
||||||
String activityPeerKey = peer.view.isBroadcastTarget()
|
|
||||||
? PeerActivityInbox.BROADCAST_PEER_KEY
|
|
||||||
: peer.view.getInstanceId();
|
|
||||||
|
|
||||||
for (GroupedPeerActions group : broker.getAllActionsFor(target)) {
|
for (GroupedPeerActions group : broker.getAllActionsFor(target)) {
|
||||||
JMenu pluginMenu = new JMenu(group.pluginDisplayName());
|
JMenu pluginMenu = new JMenu(group.pluginDisplayName());
|
||||||
for (PeerAction action : group.actions()) {
|
for (PeerAction action : group.actions()) {
|
||||||
int pending = activityPeerKey != null
|
JMenuItem item = new JMenuItem(action.getLabel());
|
||||||
? activity.countForAction(activityPeerKey, group.pluginId(), action.getId())
|
|
||||||
: 0;
|
|
||||||
boolean indicator = activityPeerKey != null
|
|
||||||
&& activity.hasIndicatorForAction(activityPeerKey, group.pluginId(), action.getId());
|
|
||||||
boolean indicatorOnly = indicator && pending == 0 && isIndicatorAction(action.getId());
|
|
||||||
JMenuItem item = new JMenuItem(
|
|
||||||
ShellUi.formatActionLabelWithBadge(action.getLabel(), pending, indicatorOnly)
|
|
||||||
);
|
|
||||||
item.addActionListener(e -> {
|
item.addActionListener(e -> {
|
||||||
if (activityPeerKey != null) {
|
|
||||||
activity.clearAction(activityPeerKey, group.pluginId(), action.getId());
|
|
||||||
}
|
|
||||||
broker.setActivePlugin(group.pluginId());
|
broker.setActivePlugin(group.pluginId());
|
||||||
action.getHandler().run(peerTarget);
|
action.getHandler().run(peerTarget);
|
||||||
});
|
});
|
||||||
@@ -141,18 +114,6 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
|
|||||||
}
|
}
|
||||||
popup.add(pluginMenu);
|
popup.add(pluginMenu);
|
||||||
}
|
}
|
||||||
if (peer.view.getNotificationCount() > 0 && !peer.view.isBroadcastTarget()) {
|
|
||||||
JMenuItem accept = new JMenuItem("Aceitar convite");
|
|
||||||
accept.addActionListener(e -> {
|
|
||||||
String pluginId = connectionManager.getInviteInboxService()
|
|
||||||
.acceptFirstInviteFrom(peer.view.getInstanceId());
|
|
||||||
if (pluginId != null) {
|
|
||||||
connectionManager.getPeerActionBroker().setActivePlugin(pluginId);
|
|
||||||
connectionManager.notifyInviteAcceptedForUi(pluginId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
popup.add(accept);
|
|
||||||
}
|
|
||||||
for (JMenu legacy : legacyCategories.values()) {
|
for (JMenu legacy : legacyCategories.values()) {
|
||||||
if (legacy.getItemCount() > 0 && target == PeerActionTarget.SINGLE_PEER && !peer.view.isBroadcastTarget()) {
|
if (legacy.getItemCount() > 0 && target == PeerActionTarget.SINGLE_PEER && !peer.view.isBroadcastTarget()) {
|
||||||
popup.addSeparator();
|
popup.addSeparator();
|
||||||
@@ -202,11 +163,7 @@ public final class NetworkUsersPanel extends JPanel implements PeerListener {
|
|||||||
record DisplayPeer(UserPeerView view) {
|
record DisplayPeer(UserPeerView view) {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
String name = view.getDisplayName();
|
return view.getDisplayName();
|
||||||
if (view.getNotificationCount() > 0) {
|
|
||||||
return name + " (" + view.getNotificationCount() + ")";
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
User toUser() {
|
User toUser() {
|
||||||
|
|||||||
@@ -101,6 +101,11 @@ public final class SessionTabBar {
|
|||||||
alignAddButtonToStrip();
|
alignAddButtonToStrip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addTabIfAbsent(String key, String title, String tooltip, JComponent content, boolean closable) {
|
||||||
|
tabs.addTabIfAbsent(key, title, tooltip, content, closable);
|
||||||
|
alignAddButtonToStrip();
|
||||||
|
}
|
||||||
|
|
||||||
public void selectTab(String key) {
|
public void selectTab(String key) {
|
||||||
tabs.selectTab(key);
|
tabs.selectTab(key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,13 @@ class SessionTabBarStripTest {
|
|||||||
bar.selectTab(PluginSessionKeys.PEER_DRAFT);
|
bar.selectTab(PluginSessionKeys.PEER_DRAFT);
|
||||||
assertEquals(PluginSessionKeys.PEER_DRAFT, bar.selectedKey());
|
assertEquals(PluginSessionKeys.PEER_DRAFT, bar.selectedKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addTabIfAbsentDoesNotChangeSelection() {
|
||||||
|
SessionTabBar bar = new SessionTabBar();
|
||||||
|
bar.addOrSelectTab(PluginSessionKeys.ROOM, "Sala geral", new JPanel(), false);
|
||||||
|
bar.addTabIfAbsent(PluginSessionKeys.PEER_DRAFT, "Nova conversa", "draft", new JPanel(), true);
|
||||||
|
assertEquals(PluginSessionKeys.ROOM, bar.selectedKey());
|
||||||
|
assertEquals(2, bar.getTabCount());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ import pitiupi.application.messaging.MessageDispatcher;
|
|||||||
import pitiupi.application.messaging.PluginTransportCatalog;
|
import pitiupi.application.messaging.PluginTransportCatalog;
|
||||||
import pitiupi.application.messaging.SendTarget;
|
import pitiupi.application.messaging.SendTarget;
|
||||||
import pitiupi.application.messaging.TransportMode;
|
import pitiupi.application.messaging.TransportMode;
|
||||||
import pitiupi.api.plugin.PeerActivityFocusTokens;
|
|
||||||
import pitiupi.application.presence.UserPeerView;
|
import pitiupi.application.presence.UserPeerView;
|
||||||
import pitiupi.chat.control.PrivateChatController;
|
import pitiupi.chat.control.PrivateChatController;
|
||||||
import pitiupi.chat.control.PrivateChatUiSink;
|
import pitiupi.chat.control.PrivateChatUiSink;
|
||||||
@@ -69,8 +68,6 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
private PrivateChatController privateChatController;
|
private PrivateChatController privateChatController;
|
||||||
private PeerDraftPanel draftPanel;
|
private PeerDraftPanel draftPanel;
|
||||||
private Runnable pendingTranscriptFlush;
|
private Runnable pendingTranscriptFlush;
|
||||||
private String pluginId;
|
|
||||||
|
|
||||||
public ChatPanel(ConnectionManager connectionManager) {
|
public ChatPanel(ConnectionManager connectionManager) {
|
||||||
this.connectionManager = connectionManager;
|
this.connectionManager = connectionManager;
|
||||||
this.routingPanel = new PluginRoutingPanel(
|
this.routingPanel = new PluginRoutingPanel(
|
||||||
@@ -102,10 +99,6 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
selectRoomSession();
|
selectRoomSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPluginId(String pluginId) {
|
|
||||||
this.pluginId = pluginId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initSessionTabs() {
|
private void initSessionTabs() {
|
||||||
sessionTabs.addOrSelectTab(
|
sessionTabs.addOrSelectTab(
|
||||||
PluginSessionKeys.ROOM,
|
PluginSessionKeys.ROOM,
|
||||||
@@ -152,7 +145,6 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
if (PluginSessionKeys.ROOM.equals(key)) {
|
if (PluginSessionKeys.ROOM.equals(key)) {
|
||||||
routingPanel.bindRoomSession();
|
routingPanel.bindRoomSession();
|
||||||
switchActiveConversation(ROOM_KEY);
|
switchActiveConversation(ROOM_KEY);
|
||||||
reportActivityFocusForRoom();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (key.startsWith("peer:") && !PluginSessionKeys.PEER_DRAFT.equals(key)) {
|
if (key.startsWith("peer:") && !PluginSessionKeys.PEER_DRAFT.equals(key)) {
|
||||||
@@ -160,40 +152,9 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
String name = peerDisplayNames.getOrDefault(ip, ip);
|
String name = peerDisplayNames.getOrDefault(ip, ip);
|
||||||
routingPanel.bindPeerSession(ip, name);
|
routingPanel.bindPeerSession(ip, name);
|
||||||
switchActiveConversation(resolveConversationKey());
|
switchActiveConversation(resolveConversationKey());
|
||||||
reportActivityFocusForPeerIp(ip);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reportActivityFocusForRoom() {
|
|
||||||
if (pluginId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connectionManager.getPeerActivityFocus()
|
|
||||||
.setFocusedChannel(pluginId, PeerActivityFocusTokens.broadcast());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reportActivityFocusForPeerIp(String ip) {
|
|
||||||
if (pluginId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connectionManager.getPeerActivityFocus()
|
|
||||||
.setFocusedChannel(pluginId, resolvePeerFocusToken(ip));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolvePeerFocusToken(String ip) {
|
|
||||||
String canonical = canonicalPeerIp(ip);
|
|
||||||
for (UserPeerView view : connectionManager.getPresenceService().listPeerViews()) {
|
|
||||||
if (view.isBroadcastTarget() || view.isSelf()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (view.getInstanceId() != null
|
|
||||||
&& view.getInetAddress().map(PeerIpv4::fromInetAddress).orElse("").equals(canonical)) {
|
|
||||||
return PeerActivityFocusTokens.peerInstance(view.getInstanceId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return PeerActivityFocusTokens.peerIp(canonical);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openDraftPeerTab() {
|
private void openDraftPeerTab() {
|
||||||
String previous = sessionTabs.selectedKey();
|
String previous = sessionTabs.selectedKey();
|
||||||
if (previous != null && !PluginSessionKeys.PEER_DRAFT.equals(previous)) {
|
if (previous != null && !PluginSessionKeys.PEER_DRAFT.equals(previous)) {
|
||||||
@@ -283,6 +244,18 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
showBodyCard(CARD_MAIN);
|
showBodyCard(CARD_MAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ensurePeerTabForIncoming(String ip, String displayName) {
|
||||||
|
String canonical = canonicalPeerIp(ip);
|
||||||
|
String resolved = SessionPeerLabels.resolveDisplayName(connectionManager, canonical, displayName);
|
||||||
|
peerDisplayNames.put(canonical, resolved);
|
||||||
|
String tabKey = PluginSessionKeys.peer(canonical);
|
||||||
|
sessionTabs.addTabIfAbsent(tabKey, resolved, canonical, new JPanel(), true);
|
||||||
|
if (tabKey.equals(sessionTabs.selectedKey())) {
|
||||||
|
routingPanel.bindPeerSession(canonical, resolved);
|
||||||
|
switchActiveConversation(resolveConversationKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String canonicalPeerIp(String ip) {
|
private static String canonicalPeerIp(String ip) {
|
||||||
return PeerIpv4.normalize(ip);
|
return PeerIpv4.normalize(ip);
|
||||||
}
|
}
|
||||||
@@ -298,7 +271,6 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void focusGeneralRoom() {
|
public void focusGeneralRoom() {
|
||||||
reportActivityFocusForRoom();
|
|
||||||
sessionTabs.selectTab(PluginSessionKeys.ROOM);
|
sessionTabs.selectTab(PluginSessionKeys.ROOM);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,9 +296,7 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
String bucket = bucketForIncoming(message);
|
String bucket = bucketForIncoming(message);
|
||||||
if (!ROOM_KEY.equals(bucket) && message.getSenderIp() != null && !message.getSenderIp().isBlank()) {
|
if (!ROOM_KEY.equals(bucket) && message.getSenderIp() != null && !message.getSenderIp().isBlank()) {
|
||||||
String canonical = canonicalPeerIp(message.getSenderIp());
|
String canonical = canonicalPeerIp(message.getSenderIp());
|
||||||
if (!sessionTabs.hasTab(PluginSessionKeys.peer(canonical))) {
|
ensurePeerTabForIncoming(canonical, message.getName());
|
||||||
openPeerTab(canonical, message.getName());
|
|
||||||
}
|
|
||||||
bucket = peerKey(canonical);
|
bucket = peerKey(canonical);
|
||||||
}
|
}
|
||||||
appendChatLine(bucket, message);
|
appendChatLine(bucket, message);
|
||||||
@@ -569,11 +539,23 @@ public class ChatPanel extends JPanel implements PrivateChatUiSink {
|
|||||||
private void prependLine(String bucket, String htmlLine) {
|
private void prependLine(String bucket, String htmlLine) {
|
||||||
StringBuilder buf = transcripts.computeIfAbsent(bucket, k -> new StringBuilder());
|
StringBuilder buf = transcripts.computeIfAbsent(bucket, k -> new StringBuilder());
|
||||||
buf.insert(0, htmlLine + "<br>");
|
buf.insert(0, htmlLine + "<br>");
|
||||||
if (bucket.equals(activeConversationKey)) {
|
if (bucket.equals(activeConversationKey) && isSessionTabShowingBucket(bucket)) {
|
||||||
scheduleFlushTranscriptToPane();
|
scheduleFlushTranscriptToPane();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSessionTabShowingBucket(String bucket) {
|
||||||
|
String key = sessionTabs.selectedKey();
|
||||||
|
if (ROOM_KEY.equals(bucket)) {
|
||||||
|
return PluginSessionKeys.ROOM.equals(key);
|
||||||
|
}
|
||||||
|
if (key != null && key.startsWith("peer:") && !PluginSessionKeys.PEER_DRAFT.equals(key)) {
|
||||||
|
String tabIp = canonicalPeerIp(key.substring("peer:".length()));
|
||||||
|
return peerKey(tabIp).equals(bucket);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void scheduleFlushTranscriptToPane() {
|
private void scheduleFlushTranscriptToPane() {
|
||||||
if (pendingTranscriptFlush != null) {
|
if (pendingTranscriptFlush != null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package pitiupi.chat.plugin;
|
|||||||
|
|
||||||
import pitiupi.api.plugin.PeerActionsRegistrar;
|
import pitiupi.api.plugin.PeerActionsRegistrar;
|
||||||
import pitiupi.api.plugin.PluginWorkspace;
|
import pitiupi.api.plugin.PluginWorkspace;
|
||||||
import pitiupi.api.plugin.PeerActivityFocusTokens;
|
|
||||||
import pitiupi.application.activity.PeerActivityInbox;
|
|
||||||
import pitiupi.application.peeractions.PeerAction;
|
import pitiupi.application.peeractions.PeerAction;
|
||||||
import pitiupi.application.peeractions.PeerActionTarget;
|
import pitiupi.application.peeractions.PeerActionTarget;
|
||||||
import pitiupi.application.peeractions.PeerTarget;
|
import pitiupi.application.peeractions.PeerTarget;
|
||||||
@@ -14,8 +12,6 @@ import pitiupi.chat.control.PrivateChatHost;
|
|||||||
import pitiupi.chat.net.ChatMessage;
|
import pitiupi.chat.net.ChatMessage;
|
||||||
import pitiupi.chat.net.LegacyMpChatMessage;
|
import pitiupi.chat.net.LegacyMpChatMessage;
|
||||||
import pitiupi.control.ConnectionManager;
|
import pitiupi.control.ConnectionManager;
|
||||||
import pitiupi.presentation.shell.LocalUserIdentity;
|
|
||||||
import pitiupi.presentation.shell.PeerIpv4;
|
|
||||||
import pitiupi.control.GuiManager;
|
import pitiupi.control.GuiManager;
|
||||||
import pitiupi.models.Messages.Message;
|
import pitiupi.models.Messages.Message;
|
||||||
import pitiupi.models.Plugin;
|
import pitiupi.models.Plugin;
|
||||||
@@ -42,7 +38,6 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
|
|||||||
this.workspace = workspace;
|
this.workspace = workspace;
|
||||||
this.privateChatController = new PrivateChatController(connectionManager, this);
|
this.privateChatController = new PrivateChatController(connectionManager, this);
|
||||||
this.chatPanel = new ChatPanel(connectionManager);
|
this.chatPanel = new ChatPanel(connectionManager);
|
||||||
chatPanel.setPluginId(getIdentifier());
|
|
||||||
chatPanel.setPrivateChatController(privateChatController);
|
chatPanel.setPrivateChatController(privateChatController);
|
||||||
privateChatController.setUiSink(chatPanel);
|
privateChatController.setUiSink(chatPanel);
|
||||||
bindShellWorkspaceContent(chatPanel);
|
bindShellWorkspaceContent(chatPanel);
|
||||||
@@ -108,7 +103,6 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
|
|||||||
@Override
|
@Override
|
||||||
public void receiveMessage(Message message) {
|
public void receiveMessage(Message message) {
|
||||||
if (message instanceof ChatMessage msg && chatPanel != null) {
|
if (message instanceof ChatMessage msg && chatPanel != null) {
|
||||||
recordChatActivity(msg);
|
|
||||||
SwingUtilities.invokeLater(() -> chatPanel.receiveChatMessage(msg));
|
SwingUtilities.invokeLater(() -> chatPanel.receiveChatMessage(msg));
|
||||||
} else if (message instanceof LegacyMpChatMessage legacy && chatPanel != null) {
|
} else if (message instanceof LegacyMpChatMessage legacy && chatPanel != null) {
|
||||||
SwingUtilities.invokeLater(() -> chatPanel.receiveLegacyMessage(
|
SwingUtilities.invokeLater(() -> chatPanel.receiveLegacyMessage(
|
||||||
@@ -120,51 +114,6 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recordChatActivity(ChatMessage msg) {
|
|
||||||
String localName = connectionManager.getUserName();
|
|
||||||
if (localName != null && msg.getName() != null && localName.equals(msg.getName().trim())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (LocalUserIdentity.isLocalSender(connectionManager, msg.getName(), msg.getSenderIp())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PeerActivityInbox inbox = connectionManager.getPeerActivityInbox();
|
|
||||||
String tag = msg.getTransportLabel();
|
|
||||||
boolean multicast = tag != null && tag.toLowerCase().contains("multicast");
|
|
||||||
if (multicast) {
|
|
||||||
inbox.recordBroadcastCounted(getIdentifier(), "chat.focus", PeerActivityFocusTokens.broadcast());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String instanceId = peerInstanceIdForActivity(msg.getSenderIp());
|
|
||||||
if (instanceId != null) {
|
|
||||||
inbox.recordCounted(
|
|
||||||
instanceId,
|
|
||||||
getIdentifier(),
|
|
||||||
"chat.peer",
|
|
||||||
PeerActivityFocusTokens.peerInstance(instanceId)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String peerInstanceIdForActivity(String ip) {
|
|
||||||
if (ip == null || ip.isBlank() || connectionManager == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String needle = PeerIpv4.normalize(ip);
|
|
||||||
if (needle.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (UserPeerView view : connectionManager.getPresenceService().listPeerViews()) {
|
|
||||||
if (view.isSelf()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (view.getInetAddress().map(PeerIpv4::fromInetAddress).orElse("").equals(needle)) {
|
|
||||||
return view.getInstanceId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean receivePrivateConnection(TcpConnection connection) {
|
public boolean receivePrivateConnection(TcpConnection connection) {
|
||||||
SwingUtilities.invokeLater(() -> privateChatController.connectionReceived(connection));
|
SwingUtilities.invokeLater(() -> privateChatController.connectionReceived(connection));
|
||||||
@@ -178,15 +127,6 @@ public class ChatPlugin extends Plugin implements PrivateChatHost {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
privateChatController.receiveMessage(connection, legacy);
|
privateChatController.receiveMessage(connection, legacy);
|
||||||
String instanceId = peerInstanceIdForActivity(PeerIpv4.fromInetAddress(connection.getAddress()));
|
|
||||||
if (instanceId != null) {
|
|
||||||
connectionManager.getPeerActivityInbox().recordCounted(
|
|
||||||
instanceId,
|
|
||||||
getIdentifier(),
|
|
||||||
"chat.private.tcp",
|
|
||||||
PeerActivityFocusTokens.peerInstance(instanceId)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -562,10 +562,11 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
receiveScopes.put(viewKey, scope != null ? scope : ScreenCastReceiveScope.BROADCAST);
|
receiveScopes.put(viewKey, scope != null ? scope : ScreenCastReceiveScope.BROADCAST);
|
||||||
resolveSenderIp(message.getName(), viewKey);
|
resolveSenderIp(message.getName(), viewKey);
|
||||||
sessionTabs.addOrSelectTab(viewKey, message.getName(), message.getName(), new JPanel(), true);
|
sessionTabs.addTabIfAbsent(viewKey, message.getName(), message.getName(), new JPanel(), true);
|
||||||
setBodyContent(viewKey, new JScrollPane(new JLabel(new ImageIcon(image))));
|
setBodyContent(viewKey, new JScrollPane(new JLabel(new ImageIcon(image))));
|
||||||
sessionTabs.selectTab(viewKey);
|
if (viewKey.equals(sessionTabs.selectedKey())) {
|
||||||
showBodyCard(viewKey);
|
showBodyCard(viewKey);
|
||||||
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
updateTabError(viewKey, message.getName(), "Erro ao ler imagem: " + ex.getMessage(), scope);
|
updateTabError(viewKey, message.getName(), "Erro ao ler imagem: " + ex.getMessage(), scope);
|
||||||
}
|
}
|
||||||
@@ -588,9 +589,11 @@ public class ScreenCastPanel extends JPanel {
|
|||||||
receiveScopes.put(tabKey, scope != null ? scope : ScreenCastReceiveScope.BROADCAST);
|
receiveScopes.put(tabKey, scope != null ? scope : ScreenCastReceiveScope.BROADCAST);
|
||||||
JLabel label = new JLabel("<html><p style='color:#c62828'>" + error + "</p></html>");
|
JLabel label = new JLabel("<html><p style='color:#c62828'>" + error + "</p></html>");
|
||||||
setBodyContent(tabKey, label);
|
setBodyContent(tabKey, label);
|
||||||
sessionTabs.addOrSelectTab(tabKey, title, title, new JPanel(), true);
|
sessionTabs.addTabIfAbsent(tabKey, title, title, new JPanel(), true);
|
||||||
|
if (tabKey.equals(sessionTabs.selectedKey())) {
|
||||||
showBodyCard(tabKey);
|
showBodyCard(tabKey);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ensureBodyPanel(String key) {
|
private void ensureBodyPanel(String key) {
|
||||||
if (!bodyPanels.containsKey(key)) {
|
if (!bodyPanels.containsKey(key)) {
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import java.util.List;
|
|||||||
|
|
||||||
import pitiupi.api.plugin.PeerActionsRegistrar;
|
import pitiupi.api.plugin.PeerActionsRegistrar;
|
||||||
import pitiupi.api.plugin.PluginWorkspace;
|
import pitiupi.api.plugin.PluginWorkspace;
|
||||||
import pitiupi.api.plugin.PeerActivityFocusTokens;
|
|
||||||
import pitiupi.application.activity.PeerActivityInbox;
|
|
||||||
import pitiupi.application.peeractions.PeerAction;
|
import pitiupi.application.peeractions.PeerAction;
|
||||||
import pitiupi.application.presence.UserPeerView;
|
import pitiupi.application.presence.UserPeerView;
|
||||||
import pitiupi.application.peeractions.PeerActionTarget;
|
import pitiupi.application.peeractions.PeerActionTarget;
|
||||||
@@ -110,7 +108,6 @@ public class ScreenCastPlugin extends Plugin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ScreenCastReceiveScope scope = classifyIncoming(msg);
|
ScreenCastReceiveScope scope = classifyIncoming(msg);
|
||||||
recordScreenCastActivity(msg, scope);
|
|
||||||
screenCastPanel.receiveScreenCastMessage(msg, scope);
|
screenCastPanel.receiveScreenCastMessage(msg, scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,27 +134,4 @@ public class ScreenCastPlugin extends Plugin {
|
|||||||
return ScreenCastReceiveScope.BROADCAST;
|
return ScreenCastReceiveScope.BROADCAST;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recordScreenCastActivity(ScreenCastMessage msg, ScreenCastReceiveScope scope) {
|
|
||||||
if (connectionManager == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PeerActivityInbox inbox = connectionManager.getPeerActivityInbox();
|
|
||||||
if (scope == ScreenCastReceiveScope.DIRECT) {
|
|
||||||
for (UserPeerView view : connectionManager.getPresenceService().listPeerViews()) {
|
|
||||||
if (!view.isBroadcastTarget()
|
|
||||||
&& msg.getName() != null
|
|
||||||
&& msg.getName().equals(view.getDisplayName())
|
|
||||||
&& view.getInstanceId() != null) {
|
|
||||||
inbox.signal(
|
|
||||||
view.getInstanceId(),
|
|
||||||
getIdentifier(),
|
|
||||||
"screencast.peer",
|
|
||||||
PeerActivityFocusTokens.peerInstance(view.getInstanceId())
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inbox.signalBroadcast(getIdentifier(), "screencast.multicast", PeerActivityFocusTokens.broadcast());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user