mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 22:25:20 +00:00
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:
@@ -155,6 +155,7 @@ protocol BitchatDelegate: AnyObject {
|
|||||||
func didConnectToPeer(_ peerID: String)
|
func didConnectToPeer(_ peerID: String)
|
||||||
func didDisconnectFromPeer(_ peerID: String)
|
func didDisconnectFromPeer(_ peerID: String)
|
||||||
func didUpdatePeerList(_ peers: [String])
|
func didUpdatePeerList(_ peers: [String])
|
||||||
|
func didReceiveRoomLeave(_ room: String, from peerID: String)
|
||||||
|
|
||||||
// Optional method to check if a fingerprint belongs to a favorite peer
|
// Optional method to check if a fingerprint belongs to a favorite peer
|
||||||
func isFavorite(fingerprint: String) -> Bool
|
func isFavorite(fingerprint: String) -> Bool
|
||||||
@@ -165,4 +166,8 @@ extension BitchatDelegate {
|
|||||||
func isFavorite(fingerprint: String) -> Bool {
|
func isFavorite(fingerprint: String) -> Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func didReceiveRoomLeave(_ room: String, from peerID: String) {
|
||||||
|
// Default empty implementation
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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) {
|
private func sendAnnouncementToPeer(_ peerID: String) {
|
||||||
guard let vm = delegate as? ChatViewModel else { return }
|
guard let vm = delegate as? ChatViewModel else { return }
|
||||||
|
|
||||||
@@ -1585,28 +1605,45 @@ class BluetoothMeshService: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case .leave:
|
case .leave:
|
||||||
if let nickname = String(data: packet.payload, encoding: .utf8),
|
if let senderID = String(data: packet.senderID.trimmingNullBytes(), encoding: .utf8) {
|
||||||
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("#") {
|
||||||
// Remove from active peers with proper locking
|
// Room leave notification
|
||||||
activePeersLock.lock()
|
bitchatLog("Received room leave notification from \(senderID) for room \(room)", category: "room")
|
||||||
activePeers.remove(senderID)
|
|
||||||
activePeersLock.unlock()
|
DispatchQueue.main.async {
|
||||||
|
self.delegate?.didReceiveRoomLeave(room, from: senderID)
|
||||||
announcedPeers.remove(senderID)
|
}
|
||||||
|
|
||||||
// Show leave message
|
// Relay if TTL > 0
|
||||||
DispatchQueue.main.async {
|
if packet.ttl > 1 {
|
||||||
self.delegate?.didDisconnectFromPeer(nickname)
|
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:
|
case .fragmentStart, .fragmentContinue, .fragmentEnd:
|
||||||
|
|||||||
@@ -132,6 +132,9 @@ class ChatViewModel: ObservableObject {
|
|||||||
joinedRooms.remove(room)
|
joinedRooms.remove(room)
|
||||||
saveJoinedRooms()
|
saveJoinedRooms()
|
||||||
|
|
||||||
|
// Send leave notification to other peers
|
||||||
|
meshService.sendRoomLeaveNotification(room)
|
||||||
|
|
||||||
// If we're currently in this room, exit to main chat
|
// If we're currently in this room, exit to main chat
|
||||||
if currentRoom == room {
|
if currentRoom == room {
|
||||||
currentRoom = nil
|
currentRoom = nil
|
||||||
@@ -630,6 +633,17 @@ class ChatViewModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension ChatViewModel: BitchatDelegate {
|
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) {
|
func didReceiveMessage(_ message: BitchatMessage) {
|
||||||
bitchatLog("Received message from \(message.sender), room: \(message.room ?? "nil"), senderPeerID: \(message.senderPeerID ?? "nil")", category: "message")
|
bitchatLog("Received message from \(message.sender), room: \(message.room ?? "nil"), senderPeerID: \(message.senderPeerID ?? "nil")", category: "message")
|
||||||
|
|
||||||
|
|||||||
@@ -644,8 +644,8 @@ struct ContentView: View {
|
|||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
// Signal strength indicator or unread message icon
|
// Signal strength indicator or unread message icon
|
||||||
if isMe {
|
if isMe {
|
||||||
Text("•")
|
Image(systemName: "person.fill")
|
||||||
.font(.system(size: 12))
|
.font(.system(size: 10))
|
||||||
.foregroundColor(textColor)
|
.foregroundColor(textColor)
|
||||||
} else if viewModel.unreadPrivateMessages.contains(peerID) {
|
} else if viewModel.unreadPrivateMessages.contains(peerID) {
|
||||||
Image(systemName: "envelope.fill")
|
Image(systemName: "envelope.fill")
|
||||||
|
|||||||
Reference in New Issue
Block a user