mirror of
https://github.com/block/buzz.git
synced 2026-11-11 11:51:13 +00:00
fix(mobile): keep New DM avatars steady across picker updates
Every state update from Dart reloads the native member picker, which built fresh cells showing the initials fallback and fetched each avatar again over the platform channel, so avatars blinked whenever results refreshed, most visibly right after foregrounding. Keep loaded avatars by key and draw them at once when a cell is rebuilt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: studio <dc2fc481343672df31a689840c09299972e704dac592e0da6f18486646a7ee3e@buzz.block.builderlab.xyz> Signed-off-by: Tom Brow <tomb@squareup.com>
This commit is contained in:
@@ -37,6 +37,12 @@ struct AddMembersIdentityTests {
|
||||
picker.tableView(picker.tableView, didSelectRowAt: path)
|
||||
}
|
||||
precondition(selected == keys, "Each distinct label must select its own pubkey")
|
||||
avatarReplies.removeAll()
|
||||
picker.update(["users": users, "selected": [], "query": "", "loading": false])
|
||||
let rebuilt = picker.tableView(picker.tableView, cellForRowAt: IndexPath(row: 0, section: 1))
|
||||
let rebuiltImage = (rebuilt.contentConfiguration as? UIListContentConfiguration)?.image
|
||||
precondition(avatarReplies.isEmpty, "A loaded avatar is not fetched again on update")
|
||||
precondition(rebuiltImage?.pngData() == UIImage(data: avatar!)?.pngData(), "A rebuilt row draws its loaded avatar at once")
|
||||
picker.update(["users": users, "selected": [users[1]], "query": "", "loading": false])
|
||||
let chosen = picker.tableView(picker.tableView, cellForRowAt: IndexPath(row: 0, section: 0))
|
||||
precondition(chosen.accessibilityLabel == "\(labels[1]), Same owner", "Selected identity retains its qualifier")
|
||||
|
||||
@@ -97,6 +97,10 @@ final class NativeAddMembersViewController: UITableViewController, UISearchResul
|
||||
private var state: [String: Any]
|
||||
private let send: (String, [String: Any]) -> Void
|
||||
private let loadAvatar: (String, String, @escaping (Data?) -> Void) -> Void
|
||||
/// Loaded avatars by key. Every state update rebuilds the cells, so a row
|
||||
/// draws its known avatar at once instead of blinking to its initial while
|
||||
/// the image comes back over the channel.
|
||||
private var avatars: [String: UIImage] = [:]
|
||||
private let search = UISearchController(searchResultsController: nil)
|
||||
private let pageColor: UIColor
|
||||
private let rowColor: UIColor
|
||||
@@ -282,12 +286,12 @@ final class NativeAddMembersViewController: UITableViewController, UISearchResul
|
||||
.compactMap { $0 }.filter { !$0.isEmpty }.joined(separator: ", ")
|
||||
content.textProperties.numberOfLines = 0
|
||||
content.secondaryTextProperties.numberOfLines = 1
|
||||
content.image = fallbackAvatar(user)
|
||||
content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
|
||||
content.imageProperties.reservedLayoutSize = CGSize(width: 40, height: 40)
|
||||
if let pubkey = user["pubkey"] as? String, let key = user["avatarKey"] as? String {
|
||||
avatarRequest = (pubkey, key)
|
||||
}
|
||||
content.image = avatarRequest.flatMap { avatars[$0.1] } ?? fallbackAvatar(user)
|
||||
content.imageProperties.maximumSize = CGSize(width: 40, height: 40)
|
||||
content.imageProperties.reservedLayoutSize = CGSize(width: 40, height: 40)
|
||||
cell.accessoryType = indexPath.section == 0 ? .checkmark : .none
|
||||
if unavailable {
|
||||
content.textProperties.color = .secondaryLabel
|
||||
@@ -303,13 +307,15 @@ final class NativeAddMembersViewController: UITableViewController, UISearchResul
|
||||
cell.accessibilityLabel = memberLabel
|
||||
cell.contentView.accessibilityElementsHidden = true
|
||||
}
|
||||
if let (pubkey, key) = avatarRequest {
|
||||
cell.accessibilityIdentifier = key
|
||||
loadAvatar(pubkey, key) { [weak cell] data in
|
||||
if let (_, key) = avatarRequest { cell.accessibilityIdentifier = key }
|
||||
if let (pubkey, key) = avatarRequest, avatars[key] == nil {
|
||||
loadAvatar(pubkey, key) { [weak self, weak cell] data in
|
||||
guard let data, let image = UIImage(data: data) else { return }
|
||||
let avatar = image.withRenderingMode(.alwaysOriginal)
|
||||
self?.avatars[key] = avatar
|
||||
guard let cell, cell.accessibilityIdentifier == key,
|
||||
let data, let image = UIImage(data: data),
|
||||
var configuration = cell.contentConfiguration as? UIListContentConfiguration else { return }
|
||||
configuration.image = image.withRenderingMode(.alwaysOriginal)
|
||||
configuration.image = avatar
|
||||
let label = cell.accessibilityLabel
|
||||
cell.contentConfiguration = configuration
|
||||
cell.accessibilityLabel = label
|
||||
|
||||
Reference in New Issue
Block a user