// // MemberInfoVC.swift // QuickLocation // import UIKit import ObjectMapper import RxSwift final class MemberInfoVC: BaseViewController { private var rootView: MemberInfoView! private var members: [GroupMemberModel] private var selectedUserId: String private var groupModel: GroupModel? private var isLoadingRelations = false init(members: [GroupMemberModel], selectedUserId: String, groupModel: GroupModel?) { self.members = members self.selectedUserId = selectedUserId self.groupModel = groupModel super.init(nibName: nil, bundle: nil) } convenience init(userInfo: [String: Any]) { let rawMembers = userInfo["members"] let membersJson: [[String: Any]] if let arr = rawMembers as? [[String: Any]] { membersJson = arr } else if let arr = rawMembers as? [Any] { membersJson = arr.compactMap { $0 as? [String: Any] } } else { membersJson = [] } let members = membersJson.compactMap { GroupMemberModel(JSON: $0) } let selected = (userInfo["userId"] as? String) ?? members.first?.user_id ?? "" let group = (userInfo["groupJson"] as? [String: Any]).flatMap { GroupModel(JSON: $0) } self.init(members: members, selectedUserId: selected, groupModel: group) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func loadView() { rootView = MemberInfoView(frame: UIScreen.main.bounds) view = rootView } override func viewDidLoad() { super.viewDidLoad() rootView.memberCV.dataSource = self rootView.memberCV.delegate = self bind() refreshDetail() rootView.memberCV.reloadData() } private func bind() { rootView.navView.onBackTapped = { [weak self] in self?.navigationController?.popViewController(animated: true) } } private func refreshDetail() { let member = members.first { $0.user_id == selectedUserId } let isSelf = selectedUserId == AppContextManager.shared.userId let joinText: String = { guard let date = member?.joinDate else { return "**" } let f = DateFormatter() f.dateFormat = "yyyy-MM-dd" return f.string(from: date) }() let commonGroupsRow = makeCommonGroupsRow(member: member) if isSelf { let sexText: String = { switch AppContextManager.shared.sex { case 1: return "男" case 2: return "女" case 0: return "保密" default: return "未设置" } }() rootView.renderRows([ MemberInfoRow(title: "头像", value: "", kind: .avatar, avatarURL: member?.head_pic) { [weak self] in self?.editAvatar() }, MemberInfoRow(title: "昵称", value: member?.nick_name ?? "", kind: .disclosure) { [weak self] in self?.editNickname() }, MemberInfoRow(title: "性别", value: sexText, kind: .disclosure) { [weak self] in self?.editGender() }, MemberInfoRow(title: "添加时间", value: joinText, kind: .text), commonGroupsRow ]) } else { rootView.renderRows([ MemberInfoRow(title: "昵称", value: member?.nick_name ?? "", kind: .text), MemberInfoRow(title: "备注", value: member?.remark ?? "", kind: .disclosure) { DLToast.show(text: "敬请期待") }, MemberInfoRow(title: "关系", value: relationText(for: member), kind: .disclosure) { [weak self] in self?.editRelation() }, MemberInfoRow(title: "性别", value: "保密", kind: .text), MemberInfoRow(title: "添加时间", value: joinText, kind: .text), commonGroupsRow ]) } } private func makeCommonGroupsRow(member: GroupMemberModel?) -> MemberInfoRow { let groups = resolvedCommonGroups(for: member) let firstName = groups.first?.group_name.trimmingCharacters(in: .whitespacesAndNewlines) ?? "" let visibleName = firstName.isEmpty ? "--" : firstName let showsMore = groups.count > 1 let action: (() -> Void)? = showsMore ? { [weak self] in self?.showCommonGroups(groups) } : nil return MemberInfoRow( title: "共同圈子", value: visibleName, kind: .tag, showsMoreTags: showsMore, action: action ) } private func resolvedCommonGroups(for member: GroupMemberModel?) -> [CommonGroupModel] { var groups = CommonGroupModel.deduplicated(member?.common_groups ?? []) let groupInfos = groupModel?.groups ?? [] for index in groups.indices where groups[index].group_name.isEmpty { groups[index].group_name = groupInfos.first { $0.group_key == groups[index].group_key }?.name ?? "未知圈子" } let currentGroup = groupInfos.first { $0.group_key == groupModel?.default_group_key } ?? groupInfos.first if let currentGroup, !currentGroup.group_key.isEmpty { if let index = groups.firstIndex(where: { $0.group_key == currentGroup.group_key }) { var group = groups.remove(at: index) if group.group_name.isEmpty || group.group_name == "未知圈子" { group.group_name = currentGroup.name } groups.insert(group, at: 0) } else { groups.insert( CommonGroupModel(groupKey: currentGroup.group_key, groupName: currentGroup.name), at: 0 ) } } return CommonGroupModel.deduplicated(groups) } private func editAvatar() { let current = AppContextManager.shared.head_pic let vc = AvatarIconListVC(iconIndex: current) vc.onSelectIcon = { [weak self] index in self?.requestSetHeadPic(index: index) } navigationController?.pushViewController(vc, animated: true) } private func editNickname() { let current = AppContextManager.shared.name let vc = TextInputViewController( title: "昵称", maxLength: 10, initialText: current ) { [weak self] text in self?.requestSetNickName(text) } present(vc, animated: true) } private func relationText(for member: GroupMemberModel?) -> String { RelationStore.shared.name(for: member?.extra.relation_idx ?? "") ?? "未绑定" } private func editRelation() { if RelationStore.shared.list.isEmpty { guard !isLoadingRelations else { return } isLoadingRelations = true DLToast.showLoading() RelationStore.shared.preload { [weak self] result in MainAsync { guard let self else { return } self.isLoadingRelations = false DLToast.dismiss() switch result { case .success: self.presentRelationPicker() case let .failure(error): DLToast.showError(text: error.localizedDescription) } } } return } presentRelationPicker() } private func presentRelationPicker() { guard presentedViewController == nil else { return } let current = members.first { $0.user_id == selectedUserId }?.extra.relation_idx ?? "" let pop = RelationSelectPopVC(currentIdx: current) { [weak self] idx in self?.requestEditRelation(idx) } present(pop, animated: true) } private func requestEditRelation(_ idx: String) { guard let member = members.first(where: { $0.user_id == selectedUserId }) else { return } let groupKey = groupModel?.default_group_key ?? "" guard !groupKey.isEmpty else { DLToast.show(text: "圈子信息缺失") return } let nick = member.remark.trimmingCharacters(in: .whitespacesAndNewlines) let nickName = nick.isEmpty ? member.nick_name : nick DLToast.showLoading() GroupService.operate( opType: "editmateinfo", requestData: [ "group_key": groupKey, "user_id": member.user_id, "nick_name": nickName, "extra": ["relation_idx": idx] ] ).subscribe(onNext: { [weak self] _ in DLToast.show(text: "设置成功") guard let self, let index = self.members.firstIndex(where: { $0.user_id == self.selectedUserId }) else { return } self.members[index].extra.relation_idx = idx self.refreshDetail() }, onError: { error in DLToast.dismiss() DLToast.show(text: error.localizedDescription) }).disposed(by: disposeBag) } private func editGender() { let pop = GenderSelectPopVC(currentSex: AppContextManager.shared.sex) { [weak self] sex in self?.requestSetGender(sex) } present(pop, animated: true) } private func requestSetHeadPic(index: Int) { DLToast.showLoading() UserService.setHeadPic(index: index).subscribe(onNext: { [weak self] _ in DLToast.show(text: "更换成功") self?.applyLocalMemberChange(headPic: "\(index)", nickName: nil) self?.syncUserConfig() }, onError: { error in DLToast.dismiss() DLToast.show(text: error.localizedDescription) }).disposed(by: disposeBag) } private func requestSetNickName(_ name: String) { let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { return } DLToast.showLoading() UserService.setNickName(nick: trimmed).subscribe(onNext: { [weak self] _ in DLToast.show(text: "修改成功") self?.applyLocalMemberChange(headPic: nil, nickName: trimmed) self?.syncUserConfig() }, onError: { error in DLToast.dismiss() DLToast.show(text: error.localizedDescription) }).disposed(by: disposeBag) } private func requestSetGender(_ sex: Int) { DLToast.showLoading() UserService.setGender(sex: sex).subscribe(onNext: { [weak self] _ in DLToast.show(text: "修改成功") if var account = AppContextManager.shared.account { account.sex = sex AppContextManager.shared.saveAccount(account) } self?.refreshDetail() self?.syncUserConfig() }, onError: { error in DLToast.dismiss() DLToast.show(text: error.localizedDescription) }).disposed(by: disposeBag) } private func applyLocalMemberChange(headPic: String?, nickName: String?) { if let index = members.firstIndex(where: { $0.user_id == selectedUserId }) { if let headPic { members[index].head_pic = headPic } if let nickName { members[index].nick_name = nickName } } if var account = AppContextManager.shared.account { if let headPic { account.head_pic = headPic } if let nickName { account.name = nickName } AppContextManager.shared.saveAccount(account) } rootView.memberCV.reloadData() refreshDetail() } private func syncUserConfig() { UserService.userInfo().subscribe(onNext: { response in if let model = response.model { AppContextManager.shared.saveAccount(model) } NotificationCenter.default.post(name: .RefreshUserConfigNotification, object: nil) }, onError: { _ in NotificationCenter.default.post(name: .RefreshUserConfigNotification, object: nil) }).disposed(by: disposeBag) } private func showCommonGroups(_ groups: [CommonGroupModel]) { guard groups.count > 1 else { return } let popup = CommonGroupsPopVC(groups: groups, groupInfos: groupModel?.groups ?? []) present(popup, animated: true) } } extension MemberInfoVC: UICollectionViewDataSource, UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { members.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell: GroupMemberListCell = collectionView.dequeueReusableCell(for: indexPath) let model = members[indexPath.item] cell.configure( model: model, isCurrentUser: model.user_id == AppContextManager.shared.userId, isSelected: model.user_id == selectedUserId ) return cell } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { selectedUserId = members[indexPath.item].user_id collectionView.reloadData() refreshDetail() } }