Address Jack review on recent DM threads in people sheet (#1576).

This commit is contained in:
Taksh 2026-07-31 17:59:30 +03:00
parent 71eeaf689f
commit 9832470868
3 changed files with 76 additions and 41 deletions

View File

@ -167,6 +167,16 @@ final class PeerListModel: ObservableObject {
}
.store(in: &cancellables)
// New/updated messages in an existing thread must refresh ordering and
// preview while the people sheet is open conversationIDs alone does
// not change for those mutations.
conversations.changes
.debounce(for: .milliseconds(120), scheduler: DispatchQueue.main)
.sink { [weak self] _ in
self?.refresh()
}
.store(in: &cancellables)
chatViewModel.groupStore.$groups
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
@ -274,7 +284,9 @@ final class PeerListModel: ObservableObject {
self.groupRows = groupRows
renderID = (
recentDirectRows.map {
"recent:\($0.id)-\($0.hasUnread)-\($0.displayName)-\($0.preview)"
// Hash the preview so a `|` inside message text cannot collide
// with the render-id separator and skip a re-render.
"recent:\($0.id)-\($0.hasUnread)-\($0.displayName)-\($0.preview.stableRenderHash)"
} +
meshRows.map {
"\($0.id)-\($0.displayName)-\($0.isConnected)-\($0.isReachable)-\($0.hasUnread)-\($0.isFavorite)-\($0.isBlocked)"
@ -295,25 +307,32 @@ final class PeerListModel: ObservableObject {
guard peerID != myPeerID else { return nil }
// Already in that thread no need to list it again here.
guard peerID != selected else { return nil }
// Match mesh rows: blocked people stay out of the people sheet.
guard !chatViewModel.isPeerBlocked(peerID) else { return nil }
let messages = messagesByPeer[peerID] ?? []
guard let last = messages.last else { return nil }
let preview = last.content
.trimmingCharacters(in: .whitespacesAndNewlines)
let clipped: String
if preview.count > 48 {
clipped = String(preview.prefix(45)) + ""
} else {
clipped = preview
}
let preview = Self.friendlyRecentPreview(last.content)
return RecentDirectRow(
peerID: peerID,
displayName: chatViewModel.nicknameForPeer(peerID),
hasUnread: chatViewModel.hasUnreadMessages(for: peerID),
preview: clipped
preview: preview
)
}
}
/// Strip media placeholder filenames (`[image] <uuid>.jpg`) down to a
/// short type marker so the recent-DM preview stays readable.
private static func friendlyRecentPreview(_ content: String) -> String {
let trimmed = content.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmed.hasPrefix("[image]") { return "[image]" }
if trimmed.hasPrefix("[voice]") { return "[voice]" }
if trimmed.count > 48 {
return String(trimmed.prefix(45)) + ""
}
return trimmed
}
private func buildGroupRows() -> [GroupChatRow] {
let myFingerprint = chatViewModel.meshService.noiseIdentityFingerprint()
return chatViewModel.groupStore.groups.map { group in
@ -352,3 +371,15 @@ final class PeerListModel: ObservableObject {
return identity.publicKeyHex.lowercased()
}
}
private extension String {
/// Stable within-process digest for render-id segments avoids embedding
/// raw message text (and `|` separators) in the joined render token.
var stableRenderHash: String {
var hash: UInt64 = 5381
for byte in utf8 {
hash = ((hash << 5) &+ hash) &+ UInt64(byte)
}
return String(hash, radix: 16)
}
}

View File

@ -39029,25 +39029,24 @@
}
},
"content.people.recent_messages" : {
"comment" : "People sheet section header for recent private message threads",
"extractionState" : "manual",
"localizations" : {
"ar" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "الرسائل الأخيرة"
}
},
"bn" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "সাম্প্রতিক বার্তা"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "letzte nachrichten"
}
},
"en" : {
@ -39059,157 +39058,157 @@
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "mensajes recientes"
}
},
"fa" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "پیام‌های اخیر"
}
},
"fil" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "mga kamakailang mensahe"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "messages récents"
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "הודעות אחרונות"
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "हाल के संदेश"
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "pesan terbaru"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "messaggi recenti"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "最近のメッセージ"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "최근 메시지"
}
},
"ms" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "mesej terbaru"
}
},
"ne" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "हालका सन्देशहरू"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "recente berichten"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "ostatnie wiadomości"
}
},
"pt" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "mensagens recentes"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "mensagens recentes"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "недавние сообщения"
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "senaste meddelanden"
}
},
"ta" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "சமீபத்திய செய்திகள்"
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "ข้อความล่าสุด"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "son mesajlar"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "нещодавні повідомлення"
}
},
"ur" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "حالیہ پیغامات"
}
},
"vi" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "tin nhắn gần đây"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "最近的消息"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "recent messages"
"value" : "最近的訊息"
}
}
}

View File

@ -1,4 +1,5 @@
import SwiftUI
import BitFoundation
/// Compact recent-DM rows for the people sheet reopen past private threads
/// without hunting through the full mesh / favorites lists (#615).
@ -49,7 +50,11 @@ struct RecentDirectMessagesList: View {
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.accessibilityLabel(row.displayName)
.accessibilityLabel(
row.hasUnread
? "\(row.displayName), \(Strings.unread)"
: row.displayName
)
.accessibilityHint(Strings.openDMHint)
}
}