PeerID 27/n: ContentView and MeshPeerList (#835)

This commit is contained in:
Islam
2025-10-19 14:42:50 +02:00
committed by GitHub
parent d4967ae9c3
commit 13b19fb8eb
2 changed files with 46 additions and 47 deletions
+40 -41
View File
@@ -80,7 +80,7 @@ struct ContentView: View {
// Timer-based refresh removed; use LocationChannelManager live updates instead
// Window sizes for rendering (infinite scroll up)
@State private var windowCountPublic: Int = 300
@State private var windowCountPrivate: [String: Int] = [:]
@State private var windowCountPrivate: [PeerID: Int] = [:]
// MARK: - Computed Properties
@@ -124,7 +124,7 @@ struct ContentView: View {
private struct PrivateHeaderContext {
let headerPeerID: String
let headerPeerID: PeerID
let peer: BitchatPeer?
let displayName: String
let isNostrAvailable: Bool
@@ -328,9 +328,9 @@ struct ContentView: View {
// MARK: - Message List View
private func messagesView(privatePeer: String?, isAtBottom: Binding<Bool>) -> some View {
private func messagesView(privatePeer: PeerID?, isAtBottom: Binding<Bool>) -> some View {
let messages: [BitchatMessage] = {
if let peerID = PeerID(str: privatePeer) {
if let peerID = privatePeer {
return viewModel.getPrivateChatMessages(for: peerID)
}
return viewModel.messages
@@ -470,7 +470,7 @@ struct ContentView: View {
}
.onChange(of: viewModel.privateChats) { _ in
if let peerID = privatePeer,
let messages = viewModel.privateChats[PeerID(str: peerID)],
let messages = viewModel.privateChats[peerID],
!messages.isEmpty {
// If the newest private message is from me, always scroll
let lastMsg = messages.last!
@@ -525,7 +525,7 @@ struct ContentView: View {
}
.onAppear {
// Also check when view appears
if let peerID = PeerID(str: privatePeer) {
if let peerID = privatePeer {
// Try multiple times to ensure read receipts are sent
viewModel.markPrivateMessagesAsRead(from: peerID)
@@ -826,10 +826,10 @@ struct ContentView: View {
}
private func scrollToBottom(on proxy: ScrollViewProxy,
privatePeer: String?,
privatePeer: PeerID?,
isAtBottom: Binding<Bool>) {
let targetID: String? = {
if let peer = PeerID(str: privatePeer),
if let peer = privatePeer,
let last = viewModel.getPrivateChatMessages(for: peer).suffix(300).last?.id {
return "dm:\(peer)|\(last)"
}
@@ -855,7 +855,7 @@ struct ContentView: View {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
let secondTarget: String? = {
if let peer = PeerID(str: privatePeer),
if let peer = privatePeer,
let last = viewModel.getPrivateChatMessages(for: peer).suffix(300).last?.id {
return "dm:\(peer)|\(last)"
}
@@ -992,14 +992,14 @@ struct ContentView: View {
textColor: textColor,
secondaryTextColor: secondaryTextColor,
onTapPeer: { peerID in
viewModel.startPrivateChat(with: PeerID(str: peerID))
viewModel.startPrivateChat(with: peerID)
showSidebar = true
},
onToggleFavorite: { peerID in
viewModel.toggleFavorite(peerID: PeerID(str: peerID))
viewModel.toggleFavorite(peerID: peerID)
},
onShowFingerprint: { peerID in
viewModel.showFingerprint(for: PeerID(str: peerID))
viewModel.showFingerprint(for: peerID)
}
)
}
@@ -1014,7 +1014,7 @@ struct ContentView: View {
private var privateChatSheetView: some View {
VStack(spacing: 0) {
if let privatePeerID = viewModel.selectedPrivateChatPeer?.id {
if let privatePeerID = viewModel.selectedPrivateChatPeer {
let headerContext = makePrivateHeaderContext(for: privatePeerID)
HStack(spacing: 12) {
@@ -1038,12 +1038,11 @@ struct ContentView: View {
HStack(spacing: 8) {
privateHeaderInfo(context: headerContext, privatePeerID: privatePeerID)
let peerID = PeerID(str: headerContext.headerPeerID)
let isFavorite = viewModel.isFavorite(peerID: peerID)
let isFavorite = viewModel.isFavorite(peerID: headerContext.headerPeerID)
if !privatePeerID.hasPrefix("nostr_") {
if !privatePeerID.isGeoDM {
Button(action: {
viewModel.toggleFavorite(peerID: peerID)
viewModel.toggleFavorite(peerID: headerContext.headerPeerID)
}) {
Image(systemName: isFavorite ? "star.fill" : "star")
.font(.bitchatSystem(size: 14))
@@ -1082,7 +1081,7 @@ struct ContentView: View {
.background(backgroundColor)
}
messagesView(privatePeer: viewModel.selectedPrivateChatPeer?.id, isAtBottom: $isAtBottomPrivate)
messagesView(privatePeer: viewModel.selectedPrivateChatPeer, isAtBottom: $isAtBottomPrivate)
.background(backgroundColor)
.frame(maxWidth: .infinity, maxHeight: .infinity)
Divider()
@@ -1104,9 +1103,9 @@ struct ContentView: View {
)
}
private func privateHeaderInfo(context: PrivateHeaderContext, privatePeerID: String) -> some View {
private func privateHeaderInfo(context: PrivateHeaderContext, privatePeerID: PeerID) -> some View {
Button(action: {
viewModel.showFingerprint(for: PeerID(str: context.headerPeerID))
viewModel.showFingerprint(for: context.headerPeerID)
}) {
HStack(spacing: 6) {
if let connectionState = context.peer?.connectionState {
@@ -1129,7 +1128,7 @@ struct ContentView: View {
case .offline:
EmptyView()
}
} else if viewModel.meshService.isPeerReachable(PeerID(str: context.headerPeerID)) {
} else if viewModel.meshService.isPeerReachable(context.headerPeerID) {
Image(systemName: "point.3.filled.connected.trianglepath.dotted")
.font(.bitchatSystem(size: 14))
.foregroundColor(textColor)
@@ -1139,7 +1138,7 @@ struct ContentView: View {
.font(.bitchatSystem(size: 14))
.foregroundColor(.purple)
.accessibilityLabel(String(localized: "content.accessibility.available_nostr", comment: "Accessibility label for Nostr-available peer indicator"))
} else if viewModel.meshService.isPeerConnected(PeerID(str: context.headerPeerID)) || viewModel.connectedPeers.contains(PeerID(str: context.headerPeerID)) {
} else if viewModel.meshService.isPeerConnected(context.headerPeerID) || viewModel.connectedPeers.contains(context.headerPeerID) {
Image(systemName: "dot.radiowaves.left.and.right")
.font(.bitchatSystem(size: 14))
.foregroundColor(textColor)
@@ -1150,14 +1149,14 @@ struct ContentView: View {
.font(.bitchatSystem(size: 16, weight: .medium, design: .monospaced))
.foregroundColor(textColor)
if !privatePeerID.hasPrefix("nostr_") {
let statusPeerID: String = {
if privatePeerID.count == 64, let short = viewModel.getShortIDForNoiseKey(privatePeerID) {
return short.id
if !privatePeerID.isGeoDM {
let statusPeerID: PeerID = {
if privatePeerID.id.count == 64, let short = viewModel.getShortIDForNoiseKey(privatePeerID.id) {
return short
}
return context.headerPeerID
}()
let encryptionStatus = viewModel.getEncryptionStatus(for: PeerID(str: statusPeerID))
let encryptionStatus = viewModel.getEncryptionStatus(for: statusPeerID)
if let icon = encryptionStatus.icon {
Image(systemName: icon)
.font(.bitchatSystem(size: 14))
@@ -1189,33 +1188,33 @@ struct ContentView: View {
.frame(height: headerHeight)
}
private func makePrivateHeaderContext(for privatePeerID: String) -> PrivateHeaderContext {
let headerPeerID: String = {
if privatePeerID.count == 64, let short = viewModel.getShortIDForNoiseKey(privatePeerID) {
return short.id
private func makePrivateHeaderContext(for privatePeerID: PeerID) -> PrivateHeaderContext {
let headerPeerID: PeerID = {
if privatePeerID.id.count == 64, let short = viewModel.getShortIDForNoiseKey(privatePeerID.id) {
return short
}
return privatePeerID
}()
let peer = viewModel.getPeer(byID: PeerID(str: headerPeerID))
let peer = viewModel.getPeer(byID: headerPeerID)
let displayName: String = {
if privatePeerID.hasPrefix("nostr_"), case .location(let ch) = locationManager.selectedChannel {
let disp = viewModel.geohashDisplayName(for: PeerID(str: privatePeerID))
if privatePeerID.isGeoDM, case .location(let ch) = locationManager.selectedChannel {
let disp = viewModel.geohashDisplayName(for: privatePeerID)
return "#\(ch.geohash)/@\(disp)"
}
if let name = peer?.displayName { return name }
if let name = viewModel.meshService.peerNickname(peerID: PeerID(str: headerPeerID)) { return name }
if let fav = FavoritesPersistenceService.shared.getFavoriteStatus(for: Data(hexString: headerPeerID) ?? Data()),
if let name = viewModel.meshService.peerNickname(peerID: headerPeerID) { return name }
if let fav = FavoritesPersistenceService.shared.getFavoriteStatus(for: Data(hexString: headerPeerID.id) ?? Data()),
!fav.peerNickname.isEmpty { return fav.peerNickname }
if headerPeerID.count == 16 {
let candidates = viewModel.identityManager.getCryptoIdentitiesByPeerIDPrefix(PeerID(str: headerPeerID))
if headerPeerID.id.count == 16 {
let candidates = viewModel.identityManager.getCryptoIdentitiesByPeerIDPrefix(headerPeerID)
if let id = candidates.first,
let social = viewModel.identityManager.getSocialIdentity(for: id.fingerprint) {
if let pet = social.localPetname, !pet.isEmpty { return pet }
if !social.claimedNickname.isEmpty { return social.claimedNickname }
}
} else if headerPeerID.count == 64, let keyData = Data(hexString: headerPeerID) {
} else if let keyData = headerPeerID.noiseKey {
let fp = keyData.sha256Fingerprint()
if let social = viewModel.identityManager.getSocialIdentity(for: fp) {
if let pet = social.localPetname, !pet.isEmpty { return pet }
@@ -1227,7 +1226,7 @@ struct ContentView: View {
let isNostrAvailable: Bool = {
guard let connectionState = peer?.connectionState else {
if let noiseKey = Data(hexString: headerPeerID),
if let noiseKey = Data(hexString: headerPeerID.id),
let favoriteStatus = FavoritesPersistenceService.shared.getFavoriteStatus(for: noiseKey),
favoriteStatus.isMutual {
return true
@@ -1746,7 +1745,7 @@ private extension ContentView {
private func expandWindow(ifNeededFor message: BitchatMessage,
allMessages: [BitchatMessage],
privatePeer: String?,
privatePeer: PeerID?,
proxy: ScrollViewProxy) {
let step = TransportConfig.uiWindowStepCount
let contextKey: String = {
+6 -6
View File
@@ -4,9 +4,9 @@ struct MeshPeerList: View {
@ObservedObject var viewModel: ChatViewModel
let textColor: Color
let secondaryTextColor: Color
let onTapPeer: (String) -> Void
let onToggleFavorite: (String) -> Void
let onShowFingerprint: (String) -> Void
let onTapPeer: (PeerID) -> Void
let onToggleFavorite: (PeerID) -> Void
let onShowFingerprint: (PeerID) -> Void
@Environment(\.colorScheme) var colorScheme
@State private var orderedIDs: [String] = []
@@ -130,7 +130,7 @@ struct MeshPeerList: View {
}
if !isMe {
Button(action: { onToggleFavorite(peer.peerID.id) }) {
Button(action: { onToggleFavorite(peer.peerID) }) {
Image(systemName: (peer.favoriteStatus?.isFavorite ?? false) ? "star.fill" : "star")
.font(.bitchatSystem(size: 12))
.foregroundColor((peer.favoriteStatus?.isFavorite ?? false) ? .yellow : secondaryTextColor)
@@ -142,8 +142,8 @@ struct MeshPeerList: View {
.padding(.vertical, 4)
.padding(.top, idx == 0 ? 10 : 0)
.contentShape(Rectangle())
.onTapGesture { if !isMe { onTapPeer(peer.peerID.id) } }
.onTapGesture(count: 2) { if !isMe { onShowFingerprint(peer.peerID.id) } }
.onTapGesture { if !isMe { onTapPeer(peer.peerID) } }
.onTapGesture(count: 2) { if !isMe { onShowFingerprint(peer.peerID) } }
}
}
// Seed and update order outside result builder