217 lines
7.7 KiB
Swift
217 lines
7.7 KiB
Swift
//
|
||
// 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 var hasSetupIMListeners = false
|
||
|
||
/// 进入页面加载 IM 数据:确保登录后并行拉取群列表 + 会话
|
||
private func loadIMData() {
|
||
GroupIMService.shared.ensureLogin { [weak self] success in
|
||
guard let self = self else { return }
|
||
guard success else {
|
||
return
|
||
}
|
||
self.setupIMListenersIfNeeded()
|
||
self.refreshIMData()
|
||
}
|
||
}
|
||
|
||
/// 注册 IM 监听(仅一次):会话变化更新未读/时间;群组变化(SDK 同步到新群/退群)刷新列表
|
||
private func setupIMListenersIfNeeded() {
|
||
guard !hasSetupIMListeners else { return }
|
||
hasSetupIMListeners = true
|
||
|
||
GroupIMService.shared.setConversationListener { [weak self] _ in
|
||
GroupIMService.shared.getConversationList { conversations in
|
||
self?.viewModel.updateConversations(conversations)
|
||
}
|
||
}
|
||
|
||
// 群组同步完成(如刚创建/加入的群)后刷新群列表,解决业务接口先返回、SDK 后同步的时序问题
|
||
GroupIMService.shared.setGroupListener { [weak self] in
|
||
GroupIMService.shared.getJoinedGroups { groups in
|
||
self?.viewModel.loadIMGroups(groups)
|
||
}
|
||
}
|
||
}
|
||
|
||
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
|
||
DLToast.dismiss()
|
||
self?.viewModel.loadIMGroups(groups, conversations: conversations)
|
||
}
|
||
}
|
||
|
||
// 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)
|
||
|
||
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)
|
||
|
||
NotificationCenter.default.rx.notification(.RefreshUserConfigNotification)
|
||
.subscribe(onNext: { [weak self] _ in
|
||
self?.requestRecommandGroup()
|
||
self?.requestGroupInfo()
|
||
|
||
guard let config = AppContextManager.shared.systemConfig else { return }
|
||
self?.rootView.cycleScrollView.imageURLStringsGroup = config.groupBannerList
|
||
})
|
||
.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<HotGroupListSectionModel> = {
|
||
RxCollectionViewSectionedReloadDataSource<HotGroupListSectionModel> { datasource, collectionView, indexPath, model in
|
||
let cell: HotGroupCell = collectionView.dequeueReusableCell(for: indexPath)
|
||
cell.configure(model)
|
||
return cell
|
||
}
|
||
}()
|
||
|
||
private lazy var createdDataSource: RxTableViewSectionedReloadDataSource<CircleListSectionModel> = {
|
||
RxTableViewSectionedReloadDataSource<CircleListSectionModel> { _, tableView, indexPath, model in
|
||
let cell: CircleGroupCell = tableView.dequeueReusableCell(for: indexPath)
|
||
cell.configure(model)
|
||
return cell
|
||
}
|
||
}()
|
||
|
||
private lazy var joinedDataSource: RxTableViewSectionedReloadDataSource<CircleListSectionModel> = {
|
||
RxTableViewSectionedReloadDataSource<CircleListSectionModel> { _, 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.loadIMData()
|
||
}.disposed(by: disposeBag)
|
||
}
|
||
}
|