From a5865af29287e82ac0d03fbdeb355d70225fd283 Mon Sep 17 00:00:00 2001 From: jack Date: Tue, 8 Jul 2025 04:00:50 +0200 Subject: [PATCH 1/2] Add screenshot detection and notification feature - Detect when users take screenshots on iOS - Send system notification to all participants - Works in public chat, channels, and private messages - Display screenshot notifications as system messages --- bitchat/ViewModels/ChatViewModel.swift | 83 ++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index e86da2bb..d1d54595 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -130,6 +130,14 @@ class ChatViewModel: ObservableObject { name: UIApplication.didBecomeActiveNotification, object: nil ) + + // Add screenshot detection for iOS + NotificationCenter.default.addObserver( + self, + selector: #selector(userDidTakeScreenshot), + name: UIApplication.userDidTakeScreenshotNotification, + object: nil + ) #endif } @@ -1058,6 +1066,66 @@ class ChatViewModel: ObservableObject { } } + @objc private func userDidTakeScreenshot() { + // Send screenshot notification based on current context + let screenshotMessage = "* \(nickname) took a screenshot *" + + if let peerID = selectedPrivateChatPeer { + // In private chat - send to the other person + if let peerNickname = meshService.getPeerNicknames()[peerID] { + meshService.sendPrivateMessage(screenshotMessage, to: peerID, recipientNickname: peerNickname) + } + + // Also show locally + let localNotification = BitchatMessage( + sender: "system", + content: "you took a screenshot", + timestamp: Date(), + isRelay: false, + originalSender: nil, + isPrivate: true, + recipientNickname: meshService.getPeerNicknames()[peerID], + senderPeerID: meshService.myPeerID + ) + if privateChats[peerID] == nil { + privateChats[peerID] = [] + } + privateChats[peerID]?.append(localNotification) + + } else if let channel = currentChannel { + // In a channel - send to channel + meshService.sendMessage(screenshotMessage, channel: channel) + + // Also show locally + let localNotification = BitchatMessage( + sender: "system", + content: "you took a screenshot", + timestamp: Date(), + isRelay: false, + originalSender: nil, + isPrivate: false, + channel: channel + ) + if channelMessages[channel] == nil { + channelMessages[channel] = [] + } + channelMessages[channel]?.append(localNotification) + + } else { + // In public chat - send to everyone + meshService.sendMessage(screenshotMessage) + + // Also show locally + let localNotification = BitchatMessage( + sender: "system", + content: "you took a screenshot", + timestamp: Date(), + isRelay: false + ) + messages.append(localNotification) + } + } + func markPrivateMessagesAsRead(from peerID: String) { // Get the nickname for this peer let peerNickname = meshService.getPeerNicknames()[peerID] ?? "" @@ -2438,9 +2506,10 @@ extension ChatViewModel: BitchatDelegate { } } - // Check if this is a hug/slap action that should be converted to system message + // Check if this is an action that should be converted to system message let isActionMessage = messageToStore.content.hasPrefix("* ") && messageToStore.content.hasSuffix(" *") && - (messageToStore.content.contains("🫂") || messageToStore.content.contains("🐟")) + (messageToStore.content.contains("🫂") || messageToStore.content.contains("🐟") || + messageToStore.content.contains("took a screenshot")) if isActionMessage { // Convert to system message @@ -2621,9 +2690,10 @@ extension ChatViewModel: BitchatDelegate { } } - // Check if this is a hug/slap action that should be converted to system message + // Check if this is an action that should be converted to system message let isActionMessage = messageToAdd.content.hasPrefix("* ") && messageToAdd.content.hasSuffix(" *") && - (messageToAdd.content.contains("🫂") || messageToAdd.content.contains("🐟")) + (messageToAdd.content.contains("🫂") || messageToAdd.content.contains("🐟") || + messageToAdd.content.contains("took a screenshot")) let finalMessage: BitchatMessage if isActionMessage { @@ -2675,9 +2745,10 @@ extension ChatViewModel: BitchatDelegate { } else { // Regular public message (main chat) - // Check if this is a hug/slap action that should be converted to system message + // Check if this is an action that should be converted to system message let isActionMessage = message.content.hasPrefix("* ") && message.content.hasSuffix(" *") && - (message.content.contains("🫂") || message.content.contains("🐟")) + (message.content.contains("🫂") || message.content.contains("🐟") || + message.content.contains("took a screenshot")) if isActionMessage { // Convert to system message From cc2f231d166e8725f8b5c46b208c52619fe84eff Mon Sep 17 00:00:00 2001 From: jack Date: Tue, 8 Jul 2025 04:07:06 +0200 Subject: [PATCH 2/2] Fix screenshot notifications and remove 'private:' from UI - Screenshot notifications now properly appear as system messages - Send messages directly via mesh service to avoid local echo - Remove 'private:' prefix from private chat header - Keep only lock icon and peer name in private chat header --- bitchat/ViewModels/ChatViewModel.swift | 16 +++++++++++----- bitchat/Views/ContentView.swift | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index d1d54595..71f54b24 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -1073,10 +1073,11 @@ class ChatViewModel: ObservableObject { if let peerID = selectedPrivateChatPeer { // In private chat - send to the other person if let peerNickname = meshService.getPeerNicknames()[peerID] { + // Send the message directly without going through sendPrivateMessage to avoid local echo meshService.sendPrivateMessage(screenshotMessage, to: peerID, recipientNickname: peerNickname) } - // Also show locally + // Show local notification immediately as system message let localNotification = BitchatMessage( sender: "system", content: "you took a screenshot", @@ -1094,9 +1095,14 @@ class ChatViewModel: ObservableObject { } else if let channel = currentChannel { // In a channel - send to channel - meshService.sendMessage(screenshotMessage, channel: channel) + // Check if channel is password protected and encrypt if needed + if let channelKey = channelKeys[channel] { + meshService.sendEncryptedChannelMessage(screenshotMessage, mentions: [], channel: channel, channelKey: channelKey) + } else { + meshService.sendMessage(screenshotMessage, mentions: [], channel: channel) + } - // Also show locally + // Show local notification immediately as system message let localNotification = BitchatMessage( sender: "system", content: "you took a screenshot", @@ -1113,9 +1119,9 @@ class ChatViewModel: ObservableObject { } else { // In public chat - send to everyone - meshService.sendMessage(screenshotMessage) + meshService.sendMessage(screenshotMessage, mentions: [], channel: nil) - // Also show locally + // Show local notification immediately as system message let localNotification = BitchatMessage( sender: "system", content: "you took a screenshot", diff --git a/bitchat/Views/ContentView.swift b/bitchat/Views/ContentView.swift index d0065c47..23e21ed8 100644 --- a/bitchat/Views/ContentView.swift +++ b/bitchat/Views/ContentView.swift @@ -190,7 +190,7 @@ struct ContentView: View { Image(systemName: "lock.fill") .font(.system(size: 14)) .foregroundColor(Color.orange) - Text("private: \(privatePeerNick)") + Text("\(privatePeerNick)") .font(.system(size: 16, weight: .medium, design: .monospaced)) .foregroundColor(Color.orange) }