Files
buzz/mobile/ios/NativeControlTests/AddMembersIdentityTests.swift
Tom Brow 0c8ec394c9 fix(mobile): diff New DM picker updates and animate picks
Flutter pushes the picker's whole state on every rebuild, and each push
reloaded every visible row with fresh, unreused cells. Drop identical
states, apply the rest as a diffable snapshot that redraws only changed
rows in reused cells, and animate a member's row moving between the
chosen and result sections when it is picked or removed.

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>
2026-11-07 16:38:58 -08:00

55 lines
3.3 KiB
Swift

import Flutter
import UIKit
@main
struct AddMembersIdentityTests {
@MainActor static func main() {
let keys = [String(repeating: "a", count: 64), String(repeating: "b", count: 64)]
let labels = ["Scout · aaaaaaaa", "Scout · bbbbbbbb"]
let users: [[String: Any]] = zip(keys, labels).map { key, label in
["pubkey": key, "name": label, "detail": "Same owner", "agent": true, "initial": "S", "avatarKey": key]
}
var selected: [String] = []
var avatarReplies: [String: (Data?) -> Void] = [:]
let avatar = UIGraphicsImageRenderer(size: CGSize(width: 40, height: 40)).image { context in
UIColor.blue.setFill()
context.fill(CGRect(x: 0, y: 0, width: 40, height: 40))
}.pngData()
let picker = NativeAddMembersViewController(
state: ["users": users, "selected": [], "query": "", "loading": false],
loadAvatar: { key, _, reply in avatarReplies[key] = reply },
send: { event, values in
if event == "toggle", let key = values["pubkey"] as? String { selected.append(key) }
}
)
picker.loadViewIfNeeded()
for index in keys.indices {
let path = IndexPath(row: index, section: 1)
let cell = picker.tableView.dataSource!.tableView(picker.tableView, cellForRowAt: path)
let content = cell.contentConfiguration as? UIListContentConfiguration
precondition(content?.text == labels[index], "Visible identity must retain its qualifier")
precondition(cell.isAccessibilityElement, "The row owns accessibility")
precondition(cell.contentView.accessibilityElementsHidden, "No duplicate content stop")
precondition(cell.accessibilityLabel == "\(labels[index]), Same owner", "VoiceOver must retain the qualifier")
avatarReplies[keys[index]]?(avatar)
precondition(cell.accessibilityLabel == "\(labels[index]), Same owner", "Avatar loading retains identity")
precondition(cell.contentView.accessibilityElementsHidden, "Avatar loading keeps one accessibility owner")
picker.tableView(picker.tableView, didSelectRowAt: path)
}
precondition(selected == keys, "Each distinct label must select its own pubkey")
avatarReplies.removeAll()
let renamed = [users[0].merging(["detail": "Renamed"]) { _, new in new }, users[1]]
picker.update(["users": renamed, "selected": [], "query": "", "loading": false])
let rebuilt = picker.tableView.dataSource!.tableView(picker.tableView, cellForRowAt: IndexPath(row: 0, section: 1))
let rebuiltContent = rebuilt.contentConfiguration as? UIListContentConfiguration
precondition(rebuiltContent?.secondaryText == "Renamed", "A changed row is redrawn")
precondition(avatarReplies.isEmpty, "A loaded avatar is not fetched again on update")
precondition(rebuiltContent?.image?.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.dataSource!.tableView(picker.tableView, cellForRowAt: IndexPath(row: 0, section: 0))
precondition(chosen.accessibilityLabel == "\(labels[1]), Same owner", "Selected identity retains its qualifier")
precondition(chosen.accessibilityTraits.contains(.selected), "Selection is exposed to VoiceOver")
print("Native member picker identity and accessibility passed")
}
}