mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-24 22:45:19 +00:00
Route command output to the conversation where the command was typed (#1363)
CommandProcessor results (/help text, errors like "unknown command", /msg confirmations) were always appended to the public timeline via addSystemMessage, so a command typed inside a DM appeared to do nothing until the user switched back to the public channel. handleCommand now routes .success/.error output to the open private chat when one is selected, falling back to the public timeline otherwise. The DM selection is read after processing so commands that switch chats (/msg) print into the conversation they just opened. Follow-up to #1354, which added /help and surfaced this routing gap. Co-authored-by: jack <jackjackbits@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
jack
Claude Fable 5
parent
e191e9c6f2
commit
66536063ca
@@ -1435,7 +1435,7 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, TransportEventDele
|
||||
|
||||
/// Processes IRC-style commands starting with '/'.
|
||||
/// - Parameter command: The full command string including the leading slash
|
||||
/// - Note: Supports commands like /nick, /msg, /who, /slap, /clear, /help
|
||||
/// - Note: Supports commands like /msg, /who, /slap, /clear, /help
|
||||
@MainActor
|
||||
func handleCommand(_ command: String) {
|
||||
let result = commandProcessor.process(command)
|
||||
@@ -1443,16 +1443,29 @@ final class ChatViewModel: ObservableObject, BitchatDelegate, TransportEventDele
|
||||
switch result {
|
||||
case .success(let message):
|
||||
if let msg = message {
|
||||
addSystemMessage(msg)
|
||||
addCommandOutput(msg)
|
||||
}
|
||||
case .error(let message):
|
||||
addSystemMessage(message)
|
||||
addCommandOutput(message)
|
||||
case .handled:
|
||||
// Command was handled, no message needed
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/// Command output belongs in the conversation where the user typed the
|
||||
/// command; the public timeline is invisible while a DM is open. The DM
|
||||
/// selection is read *after* processing so commands that switch chats
|
||||
/// (`/msg`) print into the conversation they just opened.
|
||||
@MainActor
|
||||
private func addCommandOutput(_ content: String) {
|
||||
if let peerID = selectedPrivateChatPeer {
|
||||
addLocalPrivateSystemMessage(content, to: peerID)
|
||||
} else {
|
||||
addSystemMessage(content)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Message Reception
|
||||
|
||||
@MainActor
|
||||
|
||||
@@ -263,6 +263,59 @@ struct ChatViewModelCommandTests {
|
||||
#expect(transport.sentPrivateMessages.isEmpty)
|
||||
}
|
||||
}
|
||||
|
||||
@Test @MainActor
|
||||
func handleCommand_outputRoutesToOpenPrivateChat() async {
|
||||
let (viewModel, transport) = makeTestableViewModel()
|
||||
let peerID = PeerID(str: "0000000000000002")
|
||||
transport.simulateConnect(peerID, nickname: "Alice")
|
||||
viewModel.selectedPrivateChatPeer = peerID
|
||||
|
||||
viewModel.handleCommand("/help")
|
||||
|
||||
#expect(viewModel.privateChats[peerID]?.last?.content == CommandProcessor.helpText)
|
||||
#expect(!viewModel.messages.contains { $0.content == CommandProcessor.helpText })
|
||||
}
|
||||
|
||||
@Test @MainActor
|
||||
func handleCommand_errorRoutesToOpenPrivateChat() async {
|
||||
let (viewModel, transport) = makeTestableViewModel()
|
||||
let peerID = PeerID(str: "0000000000000002")
|
||||
transport.simulateConnect(peerID, nickname: "Alice")
|
||||
viewModel.selectedPrivateChatPeer = peerID
|
||||
|
||||
viewModel.handleCommand("/bogus")
|
||||
|
||||
let dmContents = viewModel.privateChats[peerID]?.map(\.content) ?? []
|
||||
#expect(dmContents.contains { $0.hasPrefix("unknown command: /bogus") })
|
||||
#expect(!viewModel.messages.contains { $0.content.hasPrefix("unknown command: /bogus") })
|
||||
}
|
||||
|
||||
@Test @MainActor
|
||||
func handleCommand_outputRoutesToPublicTimelineWithoutOpenDM() async {
|
||||
let (viewModel, _) = makeTestableViewModel()
|
||||
|
||||
viewModel.handleCommand("/bogus")
|
||||
|
||||
#expect(viewModel.messages.last?.content.hasPrefix("unknown command: /bogus") == true)
|
||||
}
|
||||
|
||||
@Test @MainActor
|
||||
func handleCommand_msgSuccessLandsInNewlyOpenedChat() async {
|
||||
let (viewModel, transport) = makeTestableViewModel()
|
||||
let peerID = PeerID(str: "0000000000000002")
|
||||
transport.simulateConnect(peerID, nickname: "Alice")
|
||||
let resolved = await TestHelpers.waitUntil({
|
||||
viewModel.getPeerIDForNickname("Alice") == peerID
|
||||
}, timeout: TestConstants.defaultTimeout)
|
||||
#expect(resolved)
|
||||
|
||||
viewModel.handleCommand("/msg Alice")
|
||||
|
||||
#expect(viewModel.selectedPrivateChatPeer == peerID)
|
||||
#expect(viewModel.privateChats[peerID]?.last?.content == "started private chat with Alice")
|
||||
#expect(!viewModel.messages.contains { $0.content == "started private chat with Alice" })
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Composer Tests
|
||||
|
||||
Reference in New Issue
Block a user