mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 15:45:20 +00:00
PeerID 27/n: ContentView and MeshPeerList (#835)
This commit is contained in:
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user