// // GroupMemberListVM.swift // QuickLocation // // Created by 八条 on 2026/6/29. // import RxSwift import RxRelay import RxDataSources // Used by the itinerary detail page in this module. struct DrivingEventItem: IdentifiableType, Equatable { typealias Identity = String let identity: String let title: String let iconName: String let count: Int init(title: String, iconName: String, count: Int = 0) { self.identity = title self.title = title self.iconName = iconName self.count = count } } struct GroupMemberDirectorySection { let title: String let members: [GroupMemberModel] } extension GroupMemberDirectorySection: SectionModelType { typealias Item = GroupMemberModel init(original: GroupMemberDirectorySection, items: [GroupMemberModel]) { self.init(title: original.title, members: items) } var items: [GroupMemberModel] { members } } final class GroupMemberListVM { struct Output { let directoryItems: Observable<[GroupMemberDirectorySection]> } let groupKey: String let output: Output private let directoryItemsRelay = BehaviorRelay<[GroupMemberDirectorySection]>(value: []) private var owner: GroupMemberModel? private var members: [GroupMemberModel] = [] private var searchKeyword = "" init(groupKey: String) { self.groupKey = groupKey output = Output(directoryItems: directoryItemsRelay.asObservable()) } func loadData(group: GroupInfoModel, members: [GroupMemberModel]) { let sortedMembers = GroupMemberSorter.sorted( members, groupKey: group.group_key, currentUserId: AppContextManager.shared.userId ) let owner = resolvedOwner(group: group, members: sortedMembers) self.owner = owner self.members = sortedMembers.filter { $0.user_id != owner?.user_id } publishDirectory() } func updateSearchKeyword(_ keyword: String) { searchKeyword = keyword.trimmingCharacters(in: .whitespacesAndNewlines) publishDirectory() } private func resolvedOwner(group: GroupInfoModel, members: [GroupMemberModel]) -> GroupMemberModel? { members.first { GroupOwnerMatcher.matches(userId: $0.user_id, groupKey: group.group_key) } } private func publishDirectory() { let matches: (GroupMemberModel) -> Bool = { [searchKeyword] member in searchKeyword.isEmpty || member.nick_name.localizedCaseInsensitiveContains(searchKeyword) } var sections: [GroupMemberDirectorySection] = [] if let owner, matches(owner) { sections.append(GroupMemberDirectorySection(title: "圈主(1人)", members: [owner])) } let visibleMembers = members.filter(matches) if !visibleMembers.isEmpty { sections.append(GroupMemberDirectorySection(title: "圈成员(\(visibleMembers.count)人)", members: visibleMembers)) } directoryItemsRelay.accept(sections) } }