154 lines
5.4 KiB
Swift
154 lines
5.4 KiB
Swift
//
|
||
// GroupViewModel.swift
|
||
// QuickLocation
|
||
//
|
||
|
||
import Foundation
|
||
import RxSwift
|
||
import RxCocoa
|
||
import RxDataSources
|
||
import OpenIMSDK
|
||
import ObjectMapper
|
||
|
||
typealias HotGroupListSectionModel = SectionModel<String, GroupInfoModel>
|
||
typealias CircleListSectionModel = SectionModel<String, GroupCellData>
|
||
|
||
/// 群聊 cell 数据(融合群信息 + 会话信息)
|
||
struct GroupCellData {
|
||
let groupInfo: OIMGroupInfo
|
||
let unreadCount: Int
|
||
let lastMsgTime: Int // ms timestamp
|
||
let lastMsgPreview: String
|
||
let tags: [String]
|
||
let iconURL: String
|
||
let localIcon: UIImage
|
||
}
|
||
|
||
final class GroupViewModel {
|
||
|
||
struct Output {
|
||
let hotGroups: Observable<[HotGroupListSectionModel]>
|
||
let createdSections: Observable<[CircleListSectionModel]>
|
||
let joinedSections: Observable<[CircleListSectionModel]>
|
||
let segmentTitles: Driver<[String]>
|
||
}
|
||
|
||
let output: Output
|
||
|
||
private let hotGroupSectionedItems = PublishSubject<[HotGroupListSectionModel]>()
|
||
private let createdSectionsSubject = PublishSubject<[CircleListSectionModel]>()
|
||
private let joinedSectionsSubject = PublishSubject<[CircleListSectionModel]>()
|
||
|
||
let segmentTitles = BehaviorRelay<[String]>(value: ["我创建的圈子", "我加入的圈子"])
|
||
|
||
var groupList: [GroupInfoModel] = []
|
||
|
||
private var cachedGroups: [OIMGroupInfo] = []
|
||
private var cachedConversations: [OIMConversationInfo] = []
|
||
|
||
/// 当前群的本地图标
|
||
func getLocalIcon(groupId: String) -> UIImage {
|
||
groupList.first { groupId == $0.group_key }?.groupIcon ?? UIImage(named: "GroupIcon/1")!
|
||
}
|
||
|
||
/// 热门酷圈
|
||
func loadHotGroupData(_ list: [GroupInfoModel]) {
|
||
hotGroupSectionedItems.onNext(list.mapSection())
|
||
}
|
||
|
||
lazy var hotGroupCellAction: Action<GroupInfoModel, Void> = { this in
|
||
return Action { model in
|
||
AppRouter.push(Route.groupInfo, userInfo: ["groupInfo": model.toJSON()])
|
||
return .empty()
|
||
}
|
||
}(self)
|
||
|
||
/// 加载群列表(全量刷新)
|
||
func loadIMGroups(_ groups: [OIMGroupInfo], conversations: [OIMConversationInfo] = []) {
|
||
if !groups.isEmpty { cachedGroups = groups }
|
||
if !conversations.isEmpty { cachedConversations = conversations }
|
||
reloadData()
|
||
}
|
||
|
||
/// 仅更新会话元数据(未读、时间)- 消息监听回调(只返回变更的会话,需合并)
|
||
func updateConversations(_ conversations: [OIMConversationInfo]) {
|
||
var merged: [String: OIMConversationInfo] = [:]
|
||
for c in cachedConversations { if let gid = c.groupID { merged[gid] = c } }
|
||
for c in conversations { if let gid = c.groupID { merged[gid] = c } }
|
||
cachedConversations = Array(merged.values)
|
||
reloadData()
|
||
}
|
||
|
||
private func reloadData() {
|
||
let currentUserId = AppContextManager.shared.userId
|
||
let convMap: [String: OIMConversationInfo] = {
|
||
var m: [String: OIMConversationInfo] = [:]
|
||
for c in cachedConversations where c.groupID != nil && !(c.groupID?.isEmpty ?? true) {
|
||
m[c.groupID!] = c
|
||
}
|
||
return m
|
||
}()
|
||
|
||
// 「全部圈子」= 全部已加入群;「我的圈子」= 我创建的
|
||
var all: [GroupCellData] = []
|
||
var created: [GroupCellData] = []
|
||
|
||
for g in cachedGroups {
|
||
let conv = convMap[g.groupID ?? ""]
|
||
let businessGroup = groupList.first { $0.group_key == g.groupID }
|
||
let data = GroupCellData(
|
||
groupInfo: g,
|
||
unreadCount: Int(conv?.unreadCount ?? 0),
|
||
lastMsgTime: Int(conv?.latestMsgSendTime ?? 0),
|
||
lastMsgPreview: Self.previewText(from: conv?.latestMsg),
|
||
tags: Array((businessGroup?.labels ?? []).prefix(2)),
|
||
iconURL: businessGroup?.groupIconURL ?? "",
|
||
localIcon: getLocalIcon(groupId: g.groupID ?? "")
|
||
)
|
||
all.append(data)
|
||
if g.ownerUserID == currentUserId || g.creatorUserID == currentUserId {
|
||
created.append(data)
|
||
}
|
||
}
|
||
|
||
createdSectionsSubject.onNext(created.mapSection())
|
||
joinedSectionsSubject.onNext(all.mapSection())
|
||
}
|
||
|
||
private static func previewText(from msg: OIMMessageInfo?) -> String {
|
||
guard let msg else { return "" }
|
||
let body: String
|
||
switch msg.contentType {
|
||
case .image: body = "[图片]"
|
||
case .audio: body = "[语音]"
|
||
case .video: body = "[视频]"
|
||
case .file: body = "[文件]"
|
||
case .location: body = "[位置]"
|
||
case .at: body = Self.displayBody(msg.atTextElem?.text ?? "[消息]")
|
||
case .quote: body = Self.displayBody(msg.quoteElem?.text ?? "[消息]")
|
||
case .face: body = "[表情]"
|
||
default:
|
||
body = Self.displayBody(msg.textElem?.content ?? msg.content ?? "")
|
||
}
|
||
let name = msg.senderNickname ?? ""
|
||
if name.isEmpty { return body }
|
||
if body.isEmpty { return name }
|
||
return "\(name): \(body)"
|
||
}
|
||
|
||
private static func displayBody(_ text: String) -> String {
|
||
if text.hasPrefix("js_emoji:") { return "[表情]" }
|
||
return text
|
||
}
|
||
|
||
// MARK: - Init
|
||
init() {
|
||
output = Output(
|
||
hotGroups: hotGroupSectionedItems.asObservable(),
|
||
createdSections: createdSectionsSubject.asObservable(),
|
||
joinedSections: joinedSectionsSubject.asObservable(),
|
||
segmentTitles: segmentTitles.asDriver()
|
||
)
|
||
}
|
||
}
|