// // GroupViewController.swift // QuickLocation // import UIKit import RxSwift import RxCocoa import RxDataSources import SDCycleScrollView import OpenIMSDK final class GroupViewController: BaseViewController { override var isNavigationBarHidden: Bool { true } fileprivate var rootView: GroupView! private let viewModel = GroupViewModel() override func loadView() { rootView = GroupView(frame: UIScreen.main.bounds) view = rootView } override func viewDidLoad() { super.viewDidLoad() bindViewModel() reactiveAction() observeTableViews() requestRecommandGroup() requestGroupInfo() guard let config = AppContextManager.shared.systemConfig else { return } rootView.cycleScrollView.imageURLStringsGroup = config.groupBannerList } // MARK: - OpenIM private func setupOpenIM() { GroupIMService.shared.initSDK() GroupIMService.shared.login { [weak self] success in guard success else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self?.requestIMGroups(retryCount: 3) } } GroupIMService.shared.setConversationListener { [weak self] _ in GroupIMService.shared.getConversationList { conversations in self?.viewModel.updateConversations(conversations) } } } private func refreshIMData() { let group = DispatchGroup() var groups: [OIMGroupInfo] = [] var conversations: [OIMConversationInfo] = [] group.enter() GroupIMService.shared.getJoinedGroups { g in groups = g group.leave() } group.enter() GroupIMService.shared.getConversationList { c in conversations = c group.leave() } group.notify(queue: .main) { [weak self] in self?.viewModel.loadIMGroups(groups, conversations: conversations) } } private func requestIMGroups(retryCount: Int = 3) { GroupIMService.shared.getJoinedGroups { [weak self] groups in guard let self = self else { return } if groups.isEmpty && retryCount > 0 { DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { self.requestIMGroups(retryCount: retryCount - 1) } } else { self.refreshIMData() } } } // MARK: - Bindings private func bindViewModel() { viewModel.output.hotGroups .observe(on: MainScheduler.asyncInstance) .bind(to: rootView.hotGroupsCollectionView.rx.items(dataSource: hotGroupDataSource)) .disposed(by: disposeBag) viewModel.output.createdSections .observe(on: MainScheduler.asyncInstance) .bind(to: rootView.createdTableView.rx.items(dataSource: createdDataSource)) .disposed(by: disposeBag) viewModel.output.joinedSections .observe(on: MainScheduler.asyncInstance) .bind(to: rootView.joinedTableView.rx.items(dataSource: joinedDataSource)) .disposed(by: disposeBag) } private func reactiveAction() { rootView.createGroupBtn.rx.tapGesture .subscribe(onNext: { _ in AppRouter.push(Route.createGroup) }) .disposed(by: disposeBag) rootView.joinGroupBtn.rx.tapGesture .subscribe(onNext: { _ in AppRouter.push(Route.joinGroup) }) .disposed(by: disposeBag) rootView.createdTabLabel.rx.tapGesture .subscribe(onNext: { [weak self] _ in self?.switchToSegment(0) }) .disposed(by: disposeBag) rootView.joinedTabLabel.rx.tapGesture .subscribe(onNext: { [weak self] _ in self?.switchToSegment(1) }) .disposed(by: disposeBag) NotificationCenter.default.rx.notification(.RefreshGroupInfoNotification) .subscribe(onNext: { [weak self] _ in self?.requestIMGroups() }) .disposed(by: disposeBag) Observable.merge( rootView.createdTableView.rx.modelSelected(GroupCellData.self).asObservable(), rootView.joinedTableView.rx.modelSelected(GroupCellData.self).asObservable() ) .subscribe(onNext: { data in guard let groupId = data.groupInfo.groupID else { return } AppRouter.push(Route.groupChat, userInfo: ["groupId": groupId]) }) .disposed(by: disposeBag) } private func switchToSegment(_ index: Int) { rootView.selectSegment(at: index) let offset = CGPoint(x: CGFloat(index) * rootView.segmentScrollView.bounds.width, y: 0) rootView.segmentScrollView.setContentOffset(offset, animated: false) } // MARK: - 内层 tableView 滚动 private func observeTableViews() { rootView.createdTableView.rx.didScroll .observe(on: MainScheduler.asyncInstance) .subscribe(onNext: { [weak self] in guard let self = self else { return } self.rootView.handleTableViewScroll(self.rootView.createdTableView) }) .disposed(by: disposeBag) rootView.joinedTableView.rx.didScroll .observe(on: MainScheduler.asyncInstance) .subscribe(onNext: { [weak self] in guard let self = self else { return } self.rootView.handleTableViewScroll(self.rootView.joinedTableView) }) .disposed(by: disposeBag) } // MARK: - dataSource private lazy var hotGroupDataSource: RxCollectionViewSectionedReloadDataSource = { RxCollectionViewSectionedReloadDataSource { datasource, collectionView, indexPath, model in let cell: HotGroupCell = collectionView.dequeueReusableCell(for: indexPath) cell.configure(model) return cell } }() private lazy var createdDataSource: RxTableViewSectionedReloadDataSource = { RxTableViewSectionedReloadDataSource { _, tableView, indexPath, model in let cell: CircleGroupCell = tableView.dequeueReusableCell(for: indexPath) cell.configure(model) return cell } }() private lazy var joinedDataSource: RxTableViewSectionedReloadDataSource = { RxTableViewSectionedReloadDataSource { _, tableView, indexPath, model in let cell: CircleGroupCell = tableView.dequeueReusableCell(for: indexPath) cell.configure(model) return cell } }() // MARK: - API private func requestRecommandGroup() { GroupService.recommand(count: 5).subscribe(onNext: { response in self.viewModel.loadHotGroupData(response.list) }).disposed(by: disposeBag) } private func requestGroupInfo() { GroupService.groupInfo().subscribe { response in guard let model = response.model else { return } self.viewModel.groupList = model.groups self.setupOpenIM() }.disposed(by: disposeBag) } }