Implement proper room leave notifications and improve UI

- Send leave notifications when users exit rooms
- Update room member lists when peers leave rooms
- Change current user icon from dot to person.fill
- Handle room-specific leave messages via protocol
- Maintain backwards compatibility for general peer disconnection
This commit is contained in:
jack
2025-07-05 19:35:37 +02:00
parent cdb93543ba
commit 4dec23e216
4 changed files with 79 additions and 23 deletions
+5
View File
@@ -155,6 +155,7 @@ protocol BitchatDelegate: AnyObject {
func didConnectToPeer(_ peerID: String)
func didDisconnectFromPeer(_ peerID: String)
func didUpdatePeerList(_ peers: [String])
func didReceiveRoomLeave(_ room: String, from peerID: String)
// Optional method to check if a fingerprint belongs to a favorite peer
func isFavorite(fingerprint: String) -> Bool
@@ -165,4 +166,8 @@ extension BitchatDelegate {
func isFavorite(fingerprint: String) -> Bool {
return false
}
func didReceiveRoomLeave(_ room: String, from peerID: String) {
// Default empty implementation
}
}
+58 -21
View File
@@ -665,6 +665,26 @@ class BluetoothMeshService: NSObject {
}
}
func sendRoomLeaveNotification(_ room: String) {
messageQueue.async { [weak self] in
guard let self = self else { return }
// Create a leave packet with room hashtag as payload
let packet = BitchatPacket(
type: MessageType.leave.rawValue,
senderID: Data(self.myPeerID.utf8),
recipientID: SpecialRecipients.broadcast, // Broadcast to all
timestamp: UInt64(Date().timeIntervalSince1970 * 1000),
payload: Data(room.utf8), // Room hashtag as payload
signature: nil,
ttl: 3 // Short TTL for leave notifications
)
bitchatLog("Sending room leave notification for \(room)", category: "room")
self.broadcastPacket(packet)
}
}
private func sendAnnouncementToPeer(_ peerID: String) {
guard let vm = delegate as? ChatViewModel else { return }
@@ -1585,28 +1605,45 @@ class BluetoothMeshService: NSObject {
}
case .leave:
if let nickname = String(data: packet.payload, encoding: .utf8),
let senderID = String(data: packet.senderID.trimmingNullBytes(), encoding: .utf8) {
// Remove from active peers with proper locking
activePeersLock.lock()
activePeers.remove(senderID)
activePeersLock.unlock()
announcedPeers.remove(senderID)
// Show leave message
DispatchQueue.main.async {
self.delegate?.didDisconnectFromPeer(nickname)
if let senderID = String(data: packet.senderID.trimmingNullBytes(), encoding: .utf8) {
// Check if payload contains a room hashtag
if let room = String(data: packet.payload, encoding: .utf8),
room.hasPrefix("#") {
// Room leave notification
bitchatLog("Received room leave notification from \(senderID) for room \(room)", category: "room")
DispatchQueue.main.async {
self.delegate?.didReceiveRoomLeave(room, from: senderID)
}
// Relay if TTL > 0
if packet.ttl > 1 {
var relayPacket = packet
relayPacket.ttl -= 1
self.broadcastPacket(relayPacket)
}
} else {
// Legacy peer disconnect (keeping for backwards compatibility)
if let nickname = String(data: packet.payload, encoding: .utf8) {
// Remove from active peers with proper locking
activePeersLock.lock()
activePeers.remove(senderID)
activePeersLock.unlock()
announcedPeers.remove(senderID)
// Show leave message
DispatchQueue.main.async {
self.delegate?.didDisconnectFromPeer(nickname)
}
self.notifyPeerListUpdate()
// Clean up peer data
peerNicknamesLock.lock()
peerNicknames.removeValue(forKey: senderID)
peerNicknamesLock.unlock()
}
}
self.notifyPeerListUpdate()
// Clean up peer data
peerNicknamesLock.lock()
peerNicknames.removeValue(forKey: senderID)
peerNicknamesLock.unlock()
} else {
}
case .fragmentStart, .fragmentContinue, .fragmentEnd:
+14
View File
@@ -132,6 +132,9 @@ class ChatViewModel: ObservableObject {
joinedRooms.remove(room)
saveJoinedRooms()
// Send leave notification to other peers
meshService.sendRoomLeaveNotification(room)
// If we're currently in this room, exit to main chat
if currentRoom == room {
currentRoom = nil
@@ -630,6 +633,17 @@ class ChatViewModel: ObservableObject {
}
extension ChatViewModel: BitchatDelegate {
func didReceiveRoomLeave(_ room: String, from peerID: String) {
// Remove peer from room members
if roomMembers[room] != nil {
roomMembers[room]?.remove(peerID)
bitchatLog("Removed peer \(peerID) from room \(room) members", category: "room")
// Force UI update
objectWillChange.send()
}
}
func didReceiveMessage(_ message: BitchatMessage) {
bitchatLog("Received message from \(message.sender), room: \(message.room ?? "nil"), senderPeerID: \(message.senderPeerID ?? "nil")", category: "message")
+2 -2
View File
@@ -644,8 +644,8 @@ struct ContentView: View {
HStack(spacing: 8) {
// Signal strength indicator or unread message icon
if isMe {
Text("")
.font(.system(size: 12))
Image(systemName: "person.fill")
.font(.system(size: 10))
.foregroundColor(textColor)
} else if viewModel.unreadPrivateMessages.contains(peerID) {
Image(systemName: "envelope.fill")