Improve room functionality

- Fix header to show "x people / y rooms" count
- Add proper leave room functionality that cleans up data
- Track and display room members in sidebar
- Show "IN #ROOM" header when viewing a room
- Only show peers who have sent messages to current room
- Add leave button in room header alongside main button
- Ensure senderPeerID is set for room member tracking
This commit is contained in:
jack
2025-07-05 19:35:37 +02:00
parent 3d7232222c
commit f1fcf11dcc
2 changed files with 71 additions and 4 deletions
+21 -1
View File
@@ -39,6 +39,7 @@ class ChatViewModel: ObservableObject {
@Published var currentRoom: String? = nil // Currently selected room @Published var currentRoom: String? = nil // Currently selected room
@Published var roomMessages: [String: [BitchatMessage]] = [:] // room -> messages @Published var roomMessages: [String: [BitchatMessage]] = [:] // room -> messages
@Published var unreadRoomMessages: [String: Int] = [:] // room -> unread count @Published var unreadRoomMessages: [String: Int] = [:] // room -> unread count
@Published var roomMembers: [String: Set<String>] = [:] // room -> set of peer IDs who have sent messages
let meshService = BluetoothMeshService() let meshService = BluetoothMeshService()
private let userDefaults = UserDefaults.standard private let userDefaults = UserDefaults.standard
@@ -132,8 +133,10 @@ class ChatViewModel: ObservableObject {
currentRoom = nil currentRoom = nil
} }
// Keep messages for now (could clear if desired) // Clean up room data
unreadRoomMessages.removeValue(forKey: room) unreadRoomMessages.removeValue(forKey: room)
roomMessages.removeValue(forKey: room)
roomMembers.removeValue(forKey: room)
} }
func switchToRoom(_ room: String?) { func switchToRoom(_ room: String?) {
@@ -229,6 +232,9 @@ class ChatViewModel: ObservableObject {
timestamp: Date(), timestamp: Date(),
isRelay: false, isRelay: false,
originalSender: nil, originalSender: nil,
isPrivate: false,
recipientNickname: nil,
senderPeerID: meshService.myPeerID,
mentions: mentions.isEmpty ? nil : mentions, mentions: mentions.isEmpty ? nil : mentions,
room: messageRoom room: messageRoom
) )
@@ -239,6 +245,12 @@ class ChatViewModel: ObservableObject {
roomMessages[room] = [] roomMessages[room] = []
} }
roomMessages[room]?.append(message) roomMessages[room]?.append(message)
// Track ourselves as a room member
if roomMembers[room] == nil {
roomMembers[room] = []
}
roomMembers[room]?.insert(meshService.myPeerID)
} else { } else {
// Add to main messages // Add to main messages
messages.append(message) messages.append(message)
@@ -658,6 +670,14 @@ extension ChatViewModel: BitchatDelegate {
roomMessages[room]?.append(message) roomMessages[room]?.append(message)
roomMessages[room]?.sort { $0.timestamp < $1.timestamp } roomMessages[room]?.sort { $0.timestamp < $1.timestamp }
// Track room members
if roomMembers[room] == nil {
roomMembers[room] = []
}
if let senderPeerID = message.senderPeerID {
roomMembers[room]?.insert(senderPeerID)
}
// Update unread count if not currently viewing this room // Update unread count if not currently viewing this room
if currentRoom != room { if currentRoom != room {
unreadRoomMessages[room] = (unreadRoomMessages[room] ?? 0) + 1 unreadRoomMessages[room] = (unreadRoomMessages[room] ?? 0) + 1
+50 -3
View File
@@ -212,6 +212,23 @@ struct ContentView: View {
Spacer() Spacer()
// Leave room button
Button(action: {
viewModel.leaveRoom(currentRoom)
}) {
Text("leave")
.font(.system(size: 12, design: .monospaced))
.foregroundColor(Color.red)
.padding(.horizontal, 8)
.padding(.vertical, 2)
.overlay(
RoundedRectangle(cornerRadius: 4)
.stroke(Color.red.opacity(0.5), lineWidth: 1)
)
}
.buttonStyle(.plain)
// Back to main button
Button(action: { Button(action: {
viewModel.switchToRoom(nil) viewModel.switchToRoom(nil)
}) { }) {
@@ -272,7 +289,15 @@ struct ContentView: View {
} }
let otherPeersCount = viewModel.connectedPeers.filter { $0 != viewModel.meshService.myPeerID }.count let otherPeersCount = viewModel.connectedPeers.filter { $0 != viewModel.meshService.myPeerID }.count
Text(viewModel.isConnected ? "\(otherPeersCount) \(otherPeersCount == 1 ? "person" : "people")" : "alone :/") let roomCount = viewModel.joinedRooms.count
let statusText = if !viewModel.isConnected {
"alone :/"
} else if roomCount > 0 {
"\(otherPeersCount) \(otherPeersCount == 1 ? "person" : "people") / \(roomCount) \(roomCount == 1 ? "room" : "rooms")"
} else {
"\(otherPeersCount) \(otherPeersCount == 1 ? "person" : "people")"
}
Text(statusText)
.font(.system(size: 12, design: .monospaced)) .font(.system(size: 12, design: .monospaced))
.foregroundColor(viewModel.isConnected ? textColor : Color.red) .foregroundColor(viewModel.isConnected ? textColor : Color.red)
} }
@@ -524,7 +549,13 @@ struct ContentView: View {
// People section // People section
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
if !viewModel.connectedPeers.isEmpty { // Show appropriate header based on context
if let currentRoom = viewModel.currentRoom {
Text("IN \(currentRoom.uppercased())")
.font(.system(size: 11, weight: .semibold, design: .monospaced))
.foregroundColor(secondaryTextColor)
.padding(.horizontal, 12)
} else if !viewModel.connectedPeers.isEmpty {
Text("PEOPLE") Text("PEOPLE")
.font(.system(size: 11, weight: .semibold, design: .monospaced)) .font(.system(size: 11, weight: .semibold, design: .monospaced))
.foregroundColor(secondaryTextColor) .foregroundColor(secondaryTextColor)
@@ -536,13 +567,29 @@ struct ContentView: View {
.font(.system(size: 14, design: .monospaced)) .font(.system(size: 14, design: .monospaced))
.foregroundColor(secondaryTextColor) .foregroundColor(secondaryTextColor)
.padding(.horizontal) .padding(.horizontal)
} else if let currentRoom = viewModel.currentRoom,
(viewModel.roomMembers[currentRoom]?.isEmpty ?? true) {
Text("No one in this room yet")
.font(.system(size: 14, design: .monospaced))
.foregroundColor(secondaryTextColor)
.padding(.horizontal)
} else { } else {
let peerNicknames = viewModel.meshService.getPeerNicknames() let peerNicknames = viewModel.meshService.getPeerNicknames()
let peerRSSI = viewModel.meshService.getPeerRSSI() let peerRSSI = viewModel.meshService.getPeerRSSI()
let myPeerID = viewModel.meshService.myPeerID let myPeerID = viewModel.meshService.myPeerID
// Filter peers based on current room
let peersToShow = if let currentRoom = viewModel.currentRoom,
let roomMemberIDs = viewModel.roomMembers[currentRoom] {
// Show only peers who have sent messages to this room
viewModel.connectedPeers.filter { roomMemberIDs.contains($0) && $0 != myPeerID }
} else {
// Show all connected peers in main chat
viewModel.connectedPeers.filter { $0 != myPeerID }
}
// Sort peers: favorites first, then alphabetically by nickname // Sort peers: favorites first, then alphabetically by nickname
let sortedPeers = viewModel.connectedPeers.filter { $0 != myPeerID }.sorted { peer1, peer2 in let sortedPeers = peersToShow.sorted { peer1, peer2 in
let isFav1 = viewModel.isFavorite(peerID: peer1) let isFav1 = viewModel.isFavorite(peerID: peer1)
let isFav2 = viewModel.isFavorite(peerID: peer2) let isFav2 = viewModel.isFavorite(peerID: peer2)