mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 04:45:20 +00:00
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:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user