// // GroupChatVC.swift // QuickLocation // // Created by 八条 on 2026/6/4. // import UIKit import RxSwift import RxCocoa import OpenIMSDK final class GroupChatVC: BaseViewController { override var isNavigationBarHidden: Bool { true } fileprivate var rootView: GroupChatView! private let groupId: String private var msgListener: MessageListenerProxy? // MARK: - Init init(groupId: String) { self.groupId = groupId super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func loadView() { rootView = GroupChatView(frame: UIScreen.main.bounds) view = rootView } override func viewDidLoad() { super.viewDidLoad() reactiveAction() loadGroupInfo() loadMessages() setupMessageListener() } // MARK: - Message Listener private func setupMessageListener() { msgListener = MessageListenerProxy { [weak self] msg in guard let self = self, msg.groupID == self.groupId else { return } let chatMsg = self.convertMessage(msg) DispatchQueue.main.async { self.rootView.messages.append(chatMsg) self.rootView.tableView.reloadData() self.rootView.tableView.scrollToRow( at: IndexPath(row: self.rootView.messages.count - 1, section: 0), at: .bottom, animated: true ) } } OIMManager.callbacker.addAdvancedMsgListener(listener: msgListener!) } // MARK: - Load Messages private func loadMessages() { let param = OIMGetAdvancedHistoryMessageListParam() param.conversationID = "sg_\(groupId)" param.count = 30 OIMManager.manager.getAdvancedHistoryMessageList( param, onSuccess: { [weak self] result in guard let self = self, let list = result?.messageList else { return } let msgs = list.compactMap { self.convertMessage($0) }.reversed() DispatchQueue.main.async { self.rootView.messages = Array(msgs) } }, onFailure: { code, msg in print("loadMessages failed: \(code) \(msg ?? "")") }) } private func convertMessage(_ msg: OIMMessageInfo) -> ChatMessage { let isSelf = msg.isSelf() let content = msg.textElem?.content ?? "[非文本消息]" let ts = TimeInterval(msg.sendTime) / 1000.0 let lastTs = rootView.messages.last?.timestamp let showTime = lastTs == nil || (ts - lastTs!) > 300 return ChatMessage( id: msg.clientMsgID ?? UUID().uuidString, isSelf: isSelf, senderName: msg.senderNickname ?? "", content: content, timestamp: ts, showTime: showTime ) } // MARK: - Group Info private func loadGroupInfo() { OIMManager.manager.getSpecifiedGroupsInfo([groupId]) { [weak self] groups in guard let self = self, let group = groups?.first else { return } self.rootView.groupNameLabel.text = group.groupName ?? "" if let faceURL = group.faceURL, !faceURL.isEmpty { self.rootView.groupAvatarView.dl.setImage(with: faceURL) } } onFailure: { code, msg in print("getGroupInfo failed: \(code) \(msg ?? "")") } } // MARK: - Actions private func reactiveAction() { rootView.backBtn.rx.tap .subscribe(onNext: { _ in AppRouter.shared.popOrDismiss() }) .disposed(by: disposeBag) rootView.sendBtn.rx.tap .subscribe(onNext: { [weak self] _ in self?.sendMessage() }) .disposed(by: disposeBag) rootView.textField.rx.controlEvent(.editingDidEndOnExit) .subscribe(onNext: { [weak self] _ in self?.sendMessage() }) .disposed(by: disposeBag) } private func sendMessage() { guard let text = rootView.textField.text?.trimmingCharacters(in: .whitespacesAndNewlines), !text.isEmpty else { return } rootView.textField.text = "" let msg = OIMMessageInfo.createTextMessage(text) OIMManager.manager.sendMessage(msg, recvID: "", groupID: groupId, offlinePushInfo: nil, onSuccess: { [weak self] _ in guard let self = self else { return } let chatMsg = self.convertMessage(msg) DispatchQueue.main.async { self.rootView.messages.append(chatMsg) self.rootView.tableView.reloadData() self.rootView.tableView.scrollToRow( at: IndexPath(row: self.rootView.messages.count - 1, section: 0), at: .bottom, animated: true ) } }, onProgress: nil as OIMNumberCallback?, onFailure: { code, errMsg in print("send failed: \(code) \(errMsg ?? "")") }) } } // MARK: - MessageListenerProxy private class MessageListenerProxy: NSObject, OIMAdvancedMsgListener { private let handler: (OIMMessageInfo) -> Void init(handler: @escaping (OIMMessageInfo) -> Void) { self.handler = handler } func onRecvNewMessage(_ msg: OIMMessageInfo) { handler(msg) } }