diff --git a/core/src/main/groovy/com/muwire/core/chat/ChatAction.java b/core/src/main/groovy/com/muwire/core/chat/ChatAction.java index a5ef8296..43363e70 100644 --- a/core/src/main/groovy/com/muwire/core/chat/ChatAction.java +++ b/core/src/main/groovy/com/muwire/core/chat/ChatAction.java @@ -1,17 +1,20 @@ package com.muwire.core.chat; enum ChatAction { - JOIN(true, false), - LEAVE(false, false), - SAY(false, false), - LIST(true, true), - HELP(true, true), - INFO(true, true); + JOIN(true, false, true), + LEAVE(false, false, true), + SAY(false, false, true), + LIST(true, true, true), + HELP(true, true, true), + INFO(true, true, true), + JOINED(true, true, false); final boolean console; final boolean stateless; - ChatAction(boolean console, boolean stateless) { + final boolean user; + ChatAction(boolean console, boolean stateless, boolean user) { this.console = console; this.stateless = stateless; + this.user = user; } } diff --git a/core/src/main/groovy/com/muwire/core/chat/ChatConnection.groovy b/core/src/main/groovy/com/muwire/core/chat/ChatConnection.groovy index 2445caa3..d55ffa72 100644 --- a/core/src/main/groovy/com/muwire/core/chat/ChatConnection.groovy +++ b/core/src/main/groovy/com/muwire/core/chat/ChatConnection.groovy @@ -83,6 +83,11 @@ class ChatConnection implements ChatLink { running.get() } + @Override + public Persona getPersona() { + persona + } + @Override public void close() { if (!running.compareAndSet(true, false)) { @@ -199,7 +204,8 @@ class ChatConnection implements ChatLink { def event = new ChatMessageEvent( uuid : uuid, payload : payload, sender : sender, host : host, room : room, chatTime : chatTime, sig : sig) eventBus.publish(event) - incomingEvents.put(event) + if (!incoming) + incomingEvents.put(event) } private void handleLeave(def json) { diff --git a/core/src/main/groovy/com/muwire/core/chat/ChatLink.java b/core/src/main/groovy/com/muwire/core/chat/ChatLink.java index e15714ae..a2549b90 100644 --- a/core/src/main/groovy/com/muwire/core/chat/ChatLink.java +++ b/core/src/main/groovy/com/muwire/core/chat/ChatLink.java @@ -5,6 +5,7 @@ import java.io.Closeable; import com.muwire.core.Persona; public interface ChatLink extends Closeable { + public Persona getPersona(); public boolean isUp(); public void sendChat(ChatMessageEvent e); public void sendLeave(Persona p); diff --git a/core/src/main/groovy/com/muwire/core/chat/ChatServer.groovy b/core/src/main/groovy/com/muwire/core/chat/ChatServer.groovy index d99b54d9..f6ad1bdd 100644 --- a/core/src/main/groovy/com/muwire/core/chat/ChatServer.groovy +++ b/core/src/main/groovy/com/muwire/core/chat/ChatServer.groovy @@ -107,7 +107,6 @@ class ChatServer { joinRoom(client, CONSOLE) connection.start() processHelp(connection.endpoint.destination) - eventBus.publish(new ChatConnectionEvent(connection : connection, status : ChatConnectionAttemptStatus.SUCCESSFUL, persona : client)) } void onChatDisconnectionEvent(ChatDisconnectionEvent e) { @@ -185,7 +184,8 @@ class ChatServer { } if ((command.action.console && e.room != CONSOLE) || - (!command.action.console && e.room == CONSOLE)) + (!command.action.console && e.room == CONSOLE) || + !command.action.user) return switch(command.action) { @@ -205,10 +205,29 @@ class ChatServer { return connections[it.destination].sendChat(e) } + String payload = rooms[room].stream().filter({it != e.sender}).map({it.toBase64()}) + .collect(Collectors.joining(",")) + if (payload.length() == 0) { + return + } + payload = "/JOINED $payload" + long now = System.currentTimeMillis() + UUID uuid = UUID.randomUUID() + byte [] sig = ChatConnection.sign(uuid, now, room, payload, me, me, spk) + ChatMessageEvent echo = new ChatMessageEvent( + uuid : uuid, + payload : payload, + sender : me, + host : me, + room : room, + chatTime : now, + sig : sig + ) + connections[e.sender.destination].sendChat(echo) } private void processLeave(ChatMessageEvent e) { - leaveRoom(e.room) + leaveRoom(e.sender, e.room) rooms.getOrDefault(e.room, []).each { if (it == e.sender) return diff --git a/core/src/main/groovy/com/muwire/core/chat/LocalChatLink.groovy b/core/src/main/groovy/com/muwire/core/chat/LocalChatLink.groovy index d66c2e8e..d41f5a69 100644 --- a/core/src/main/groovy/com/muwire/core/chat/LocalChatLink.groovy +++ b/core/src/main/groovy/com/muwire/core/chat/LocalChatLink.groovy @@ -42,4 +42,8 @@ class LocalChatLink implements ChatLink { public boolean isUp() { true } + + public Persona getPersona() { + null + } } diff --git a/gui/griffon-app/controllers/com/muwire/gui/ChatRoomController.groovy b/gui/griffon-app/controllers/com/muwire/gui/ChatRoomController.groovy index 456fd1b4..65c560b8 100644 --- a/gui/griffon-app/controllers/com/muwire/gui/ChatRoomController.groovy +++ b/gui/griffon-app/controllers/com/muwire/gui/ChatRoomController.groovy @@ -6,6 +6,7 @@ import griffon.inject.MVCMember import griffon.metadata.ArtifactProviderFor import groovy.util.logging.Log import net.i2p.crypto.DSAEngine +import net.i2p.data.Base64 import net.i2p.data.DataHelper import net.i2p.data.Signature @@ -13,6 +14,7 @@ import java.nio.charset.StandardCharsets import java.util.logging.Level import javax.annotation.Nonnull +import javax.swing.JOptionPane import com.muwire.core.Persona import com.muwire.core.chat.ChatCommand @@ -43,6 +45,36 @@ class ChatRoomController { command = new ChatCommand("/SAY $words") } + if (!command.action.user) { + JOptionPane.showMessageDialog(null, "$words is not a user command","Invalid Command", JOptionPane.ERROR_MESSAGE) + return + } + + if (command.action == ChatAction.SAY && command.payload.length() > 0) { + String toShow = DataHelper.formatTime(now) + " <" + model.core.me.getHumanReadableName() + "> "+command.payload + + view.roomTextArea.append(toShow) + view.roomTextArea.append('\n') + } + + if (command.action == ChatAction.JOIN) { + String newRoom = command.payload + if (!mvcGroup.parentGroup.childrenGroups.containsKey(newRoom)) { + def params = [:] + params['core'] = model.core + params['tabName'] = model.host.getHumanReadableName() + "-chat-rooms" + params['room'] = newRoom + params['console'] = false + params['host'] = model.host + + mvcGroup.parentGroup.createMVCGroup("chat-room", newRoom, params) + } + } + if (command.action == ChatAction.LEAVE && !model.console) { + leftRoom = true + view.closeTab.call() + } + long now = System.currentTimeMillis() UUID uuid = UUID.randomUUID() String room = model.console ? ChatServer.CONSOLE : model.room @@ -58,30 +90,6 @@ class ChatRoomController { sig : sig) model.core.eventBus.publish(event) - if (command.action == ChatAction.SAY && command.payload.length() > 0) { - String toShow = DataHelper.formatTime(now) + " <" + model.core.me.getHumanReadableName() + "> "+command.payload - - view.roomTextArea.append(toShow) - view.roomTextArea.append('\n') - } - - if (command.action == ChatAction.JOIN) { - String newRoom = command.payload - if (mvcGroup.parentGroup.childrenGroups.containsKey(newRoom)) - return - def params = [:] - params['core'] = model.core - params['tabName'] = model.host.getHumanReadableName() + "-chat-rooms" - params['room'] = newRoom - params['console'] = false - params['host'] = model.host - - mvcGroup.parentGroup.createMVCGroup("chat-room", newRoom, params) - } - if (command.action == ChatAction.LEAVE && !model.console) { - leftRoom = true - view.closeTab.call() - } } void leaveRoom() { @@ -109,16 +117,16 @@ class ChatRoomController { log.log(Level.WARNING,"bad chat command",bad) return } - + log.info("$model.room processing $command.action") switch(command.action) { case ChatAction.SAY : processSay(e, command.payload);break case ChatAction.JOIN : processJoin(e.timestamp, e.sender); break + case ChatAction.JOINED : processJoined(command.payload); break case ChatAction.LEAVE : processLeave(e.timestamp, e.sender); break } } private void processSay(ChatMessageEvent e, String text) { - log.info "processing say $text" String toDisplay = DataHelper.formatTime(e.timestamp) + " <"+e.sender.getHumanReadableName()+"> " + text + "\n" runInsideUIAsync { view.roomTextArea.append(toDisplay) @@ -128,14 +136,28 @@ class ChatRoomController { private void processJoin(long timestamp, Persona p) { String toDisplay = DataHelper.formatTime(timestamp) + " " + p.getHumanReadableName() + " joined the room\n" runInsideUIAsync { + model.members.add(p) view.roomTextArea.append(toDisplay) + view.membersTable?.model?.fireTableDataChanged() + } + } + + private void processJoined(String list) { + runInsideUIAsync { + list.split(",").each { + Persona p = new Persona(new ByteArrayInputStream(Base64.decode(it))) + model.members.add(p) + } + view.membersTable?.model?.fireTableDataChanged() } } private void processLeave(long timestamp, Persona p) { String toDisplay = DataHelper.formatTime(timestamp) + " " + p.getHumanReadableName() + " left the room\n" runInsideUIAsync { + model.members.remove(p) view.roomTextArea.append(toDisplay) + view.membersTable?.model?.fireTableDataChanged() } } diff --git a/gui/griffon-app/models/com/muwire/gui/ChatRoomModel.groovy b/gui/griffon-app/models/com/muwire/gui/ChatRoomModel.groovy index 0c777bdf..aadfd3da 100644 --- a/gui/griffon-app/models/com/muwire/gui/ChatRoomModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/ChatRoomModel.groovy @@ -16,4 +16,5 @@ class ChatRoomModel { boolean console def members = [] + } \ No newline at end of file diff --git a/gui/griffon-app/models/com/muwire/gui/ChatServerModel.groovy b/gui/griffon-app/models/com/muwire/gui/ChatServerModel.groovy index f7bb1fc0..beb2b2d1 100644 --- a/gui/griffon-app/models/com/muwire/gui/ChatServerModel.groovy +++ b/gui/griffon-app/models/com/muwire/gui/ChatServerModel.groovy @@ -50,11 +50,17 @@ class ChatServerModel { } void onChatConnectionEvent(ChatConnectionEvent e) { - if (e.connection != null) - link = e.connection runInsideUIAsync { status = e.status } + + ChatLink link = e.connection + if (link == null) + return + if (link.getPersona() == host) + this.link = link + else if (link.getPersona() == null && host == core.me) + this.link = link } private void eventLoop() { @@ -87,7 +93,6 @@ class ChatServerModel { if (chatCommand.action == ChatAction.JOIN) { room = chatCommand.payload } - log.info("dispatching to room ${room}") mvcGroup.childrenGroups[room]?.controller?.handleChatMessage(e) } diff --git a/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy b/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy index 22f1996a..6a77c7e6 100644 --- a/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy +++ b/gui/griffon-app/views/com/muwire/gui/ChatRoomView.groovy @@ -24,6 +24,7 @@ class ChatRoomView { def parent def sayField def roomTextArea + def membersTable void initUI() { int rowHeight = application.context.get("row-height") @@ -51,7 +52,7 @@ class ChatRoomView { panel { gridLayout(rows : 1, cols : 1) scrollPane { - table(autoCreateRowSorter : true, rowHeight : rowHeight) { + membersTable = table(autoCreateRowSorter : true, rowHeight : rowHeight) { tableModel(list : model.members) { closureColumn(header : "Name", preferredWidth: 100, type: String, read : {it.getHumanReadableName()}) closureColumn(header : "Trust Status", preferredWidth: 30, type : String, read : {String.valueOf(model.core.trustService.getLevel(it.destination))})