Visual_Novel_iOS/crush/Crush/Src/Modules/Chat/Util/IMManager.swift

525 lines
16 KiB
Swift
Raw Normal View History

2025-10-09 10:29:35 +00:00
//
// IMManager.swift
// Crush
//
// Created by Leon on 2025/8/18.
//
import NIMSDK
import Combine
import UserNotifications
class IMManager: NSObject {
public static let shared = IMManager()
var retryCount = 0
// appkey
var imInfo: IMConfigInfo?
// oc
var accountId: String {
imInfo?.accountId ?? ""
}
// session Cache
var cacheSessions = [String]()
// Config
var notNeedSendNotification = false
// UnreadCount
var totalUnreadCount: Int {
return chatTabAllUnreadCount
}
/// 🔥 chat tab
@Published var chatTabAllUnreadCount: Int = 0
/// 🔥notice center
@Published var noticeUnreadCount: Int = 0
@Published var noticeStat: MessageStat?
/// 🔥IM
@Published var sessionUnreadCount: Int = 0
private var cancellables = Set<AnyCancellable>()
override init() {
super.init()
setupDefaultConfig()
setupEvent()
}
func setupEvent() {
NotificationCenter.default.addObserver(self, selector: #selector(notiLoginSuccess), name: AppNotificationName.userLoginSuccess.notificationName, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(notiLogout), name: AppNotificationName.userLogout.notificationName, object: nil)
//
// UserCore.shared.isLogin()
Publishers.CombineLatest($noticeUnreadCount, $sessionUnreadCount)
.map { $0 + $1 }
.assign(to: \.chatTabAllUnreadCount, on: self)
.store(in: &cancellables)
$chatTabAllUnreadCount.sink { count in
UIApplication.shared.applicationIconBadgeNumber = count
}.store(in: &cancellables)
}
// sdk
func setupDefaultConfig() {
let config = NIMSDKConfig.shared()
// config.delegate = self
//
config.shouldSyncUnreadCount = true
//
config.maxAutoLoginRetryTimes = 10
// log
config.maximumLogDays = 30
//
config.shouldCountTeamNotification = true
//
config.animatedImageThumbnailEnabled = false
//
config.fetchAttachmentAutomaticallyAfterReceiving = false
//
config.fetchAttachmentAutomaticallyAfterReceivingInChatroom = true
//
config.asyncLoadRecentSessionEnabled = true
// 退
config.reconnectInBackgroundStateDisabled = true
// 使
config.shouldSyncStickTopSessionInfos = false
}
/// 🔥SDK
public func setupNIM() {
NIMSDK.shared().v2LoginService.add(self)
NIMSDK.shared().v2ConversationService.add(self)
NIMSDK.shared().v2SubscriptionService.add(self)
NIMSDK.shared().v2MessageService.add(self)
// SDK
v2InitailSDK()
refreshIMServerInfoConfig()
//setupNIMAppKey()
}
func v2InitailSDK(){
// guard UserCore.shared.isLogin() else {
// return
// }
let appkey = "2d6abc076f434fc52320c7118de5fead"
var option = NIMSDKOption.init(appKey: appkey)
#if DEBUG
option.apnsCername = "developerpush"
#else
option.apnsCername = "distributionpush"
#endif
let v2Option = V2NIMSDKOption()
v2Option.enableV2CloudConversation = true
NIMSDK.shared().register(withOptionV2: option, v2Option: v2Option)
if NIMSDK.shared().loginManager.isLogined(){
dlog("💬❌ NIMSDK 已登录")
}
// v2AutoLogin()
}
func v2AutoLogin(){
if imInfo == nil {
//
if let info = AppCache.fetchCache(key: CacheKey.ImAppkeyInfo.rawValue, type: IMConfigInfo.self) {
imInfo = info
} else {
return
}
}
guard UserCore.shared.isLogin() else { return }
guard let info = imInfo else { return }
guard let account = info.accountId else { return }
guard let imToken = info.token else { return }
let option = V2NIMLoginOption()
option.retryCount = 3
NIMSDK.shared().v2LoginService.login(account, token: imToken, option: option) {[weak self] in
// dlog("Log Success")
self?.regetConversationAllUnreadCount()
}
}
func logoutIM(){
NIMSDK.shared().v2LoginService.logout {
dlog("Log out")
} failure: { error in
dlog("Log error: \(error)")
}
}
/// IM
private func clearLogData() {
resetCount()
UserCore.shared.logout()
NotificationCenter.post(name: .presentSignInVc, object: nil, userInfo: nil)
}
func handleReceiveCreate(recentSession: V2NIMConversation) {
guard let message = recentSession.lastMessage else { return }
dealNotice(message: message)
}
///
func handleReveiceUpdate(recentSession: V2NIMConversation) {
guard let message = recentSession.lastMessage else { return }
if let remoteExt = message.serverExtension{
// if let type = remoteExt["type"] as? String{
// if type == MessageTypeStringCustom || type == MessageTypeStringNormal, let session = recentSession.session{
// //
// dealPhoneCallMessage(message: message, session: session)
// }
// }
}else if message.messageType == .MESSAGE_TYPE_CUSTOM {
//
}
if message.messageType == .MESSAGE_TYPE_CUSTOM{
}else{
dealNotice(message: message)
}
}
func dealNotice(message: V2NIMLastMessage) {
// NIMMessage
guard UIApplication.shared.applicationState == .active else {
return
}
let generator = UIImpactFeedbackGenerator(style: .medium)
generator.impactOccurred()
}
}
// loadData
extension IMManager {
/// app
func refreshIMServerInfoConfig() {
guard UserCore.shared.isLogin() else {
return
}
guard retryCount <= 5 else {
//
return
}
IMProvider.request(.getIMAccount, modelType: IMConfigInfo.self) {[weak self] result in
switch result {
case let .success(model):
if model != nil {
self?.imInfo = model
AppCache.cache(key: CacheKey.ImAppkeyInfo.rawValue, value: model)
//self?.setupNIMAppKey()
self?.v2AutoLogin() //
self?.retryCount = 0
}
if String.realEmpty(str: model?.accountId) {
//
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self?.refreshIMServerInfoConfig()
self?.retryCount += 1
}
}
case let .failure(error):
dlog(error)
//
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self?.refreshIMServerInfoConfig()
self?.retryCount += 1
}
}
}
}
public func retryRefreshConfig() {
if NIMSDK.shared().loginManager.isLogined() {
return
}
//
retryCount = 0
refreshIMServerInfoConfig()
}
}
// MARK: 🔴Unread Count
extension IMManager {
public func regetAllUnreadCount(){
regetNoticeUnread()
regetConversationAllUnreadCount()
}
///
func regetNoticeUnread(completion:((Bool, MessageStat?)-> Void)? = nil){
guard UserCore.shared.isLogin() else{
completion?(false, nil)
return
}
IMProvider.request(.messageStat, modelType: MessageStat.self) {[weak self] result in
switch result {
case .success(let model):
self?.noticeStat = model
self?.noticeUnreadCount = model?.unRead ?? 0
completion?(true, model)
case .failure:
break
}
}
}
public func regetConversationAllUnreadCount(){
//
sessionUnreadCount = NIMSDK.shared().v2ConversationService.getTotalUnreadCount()
dlog("🚩IMManager reget unread count:\(sessionUnreadCount)")
}
func registerNIMCount(){
let filter = V2NIMConversationFilter()
filter.conversationTypes = [1]//V2NIMConversationType.CONVERSATION_TYPE_P2P
filter.ignoreMuted = true
NIMSDK.shared().v2ConversationService.subscribeUnreadCount(by: filter)
// Result can see in onUnreadCountChangedByFilter
}
/// 🔥conversationIds
func clearUnreadCountBy(ids:[String]){
guard ids.count > 0 else{ return }
NIMSDK.shared().v2ConversationService.clearUnreadCount(byIds: ids) {[weak self] resuls in
let getUnreadcount = NIMSDK.shared().v2ConversationService.getTotalUnreadCount()
// dlog("💬getTotalUnreadCount: \(getUnreadcount)")
self?.sessionUnreadCount = getUnreadcount
}
}
func clearAllUnread(){
NIMSDK.shared().v2ConversationService.clearTotalUnreadCount {
dlog("Clear all unread message ok✅")
}
}
//
func resetCount() {
notNeedSendNotification = true
sessionUnreadCount = 0
noticeUnreadCount = 0
notNeedSendNotification = false
}
}
extension IMManager: V2NIMLoginListener{
func onLoginStatus(_ status: V2NIMLoginStatus) {
dlog("login status:\(status)")
if status == .LOGIN_STATUS_LOGINED{
//
dlog("login success")
//
registerNIMCount()
regetConversationAllUnreadCount()
regetNoticeUnread()
}
}
func onKickedOffline(_ detail: V2NIMKickedOfflineDetail) {
//
guard UserCore.shared.isLogin() else {
return
}
let reason = "Your account has signed in through another device"
let alert = Alert(title: "Session Interrupted", text: reason)
let action1 = AlertAction(title: "Got it", actionStyle: .confirm) {[weak self] in
self?.clearLogData()
}
alert.addAction(action1)
alert.show()
}
func onLoginFailed(_ error: V2NIMError) {
dlog("onAutoLoginFailed__ \(error.description)")
if error.code == 417 {
guard let _ = imInfo?.accountId, let _ = imInfo?.token else { return }
v2AutoLogin()
}
}
}
// MARK: NIMConversationManagerDelegate
extension IMManager: V2NIMConversationListener{
func onSyncStarted() {
dlog("onSyncStarted")
}
func onSyncFinished() {
dlog("onSyncFinished")
}
func onSyncFailed(_ error: V2NIMError) {
dlog("onSyncFailed:\(error)")
}
func onConversationCreated(_ conversation: V2NIMConversation) {
handleReveiceUpdate(recentSession: conversation)
}
func onConversationChanged(_ conversations: [V2NIMConversation]) {
for per in conversations{
handleReveiceUpdate(recentSession: per)
}
}
func onConversationDeleted(_ conversationIds: [String]) {
dlog("💬🗑onConversationDeleted: \(conversationIds)")
}
func onTotalUnreadCountChanged(_ unreadCount: Int) {
dlog("💬onTotalUnreadCountChanged : \(unreadCount)")
sessionUnreadCount = unreadCount
}
//
func onConversationReadTimeUpdated(_ conversationId: String, readTime: TimeInterval) {
}
func onUnreadCountChanged(by filter: V2NIMConversationFilter, unreadCount: Int) {
// dlog("unreadCount:\(unreadCount)")
}
}
extension IMManager : V2NIMSubscribeListener{
///
func onUserStatusChanged(_ data: [V2NIMUserStatus]) {
// none
dlog("onUserStatusChanged\(data)")
}
}
extension IMManager : V2NIMMessageListener{
func onReceive(_ messages: [V2NIMMessage]) {
// ...
regetConversationAllUnreadCount()
}
}
// MARK: Notification
extension IMManager {
@objc func notiLoginSuccess() {
retryCount = 0
refreshIMServerInfoConfig()
UNUserNotificationCenter.current().requestAuthorization(options: [.badge, .sound, .alert]) { granted, error in
print("Notification granted: \(granted), error: \(String(describing: error))")
}
}
@objc func notiLogout() {
imInfo = nil
resetCount()
removeAllCache()
NIMSDK.shared().v2LoginService.logout { [weak self] in
self?.resetCount()
}
}
}
// public cache
extension IMManager {
public func addCache(sessionID: String) {
if !cacheSessions.contains(sessionID) {
cacheSessions.append(sessionID)
}
}
public func deleteCache(sessionID: String) {
if cacheSessions.contains(sessionID) {
cacheSessions.removeObj(sessionID)
}
}
func findCache(sessionID: String) -> Bool{
return cacheSessions.contains(sessionID)
}
func removeAllCache() {
cacheSessions.removeAll()
}
}
//
extension IMManager {
/// , account IDuserID
static func sendMessage(msgText: String, accID:String) {
if String.realEmpty(str: msgText.trimmed) {
//
return
}
//
let message = IMMessageMaker.msgWithText(msgText)
//
let canSend = IMManager.dealWillSendMessage(message: message)
if canSend {
//
// let session = NIMSession(accID, type: .P2P)
// NIMSDK.shared().chatManager.send(message, to: session) { error in
// //
// #warning("test action")
// }
} else {
//
}
}
//
static public func dealWillSendMessage(message: V2NIMMessage) -> Bool {
// var dict = [String:String]()
// if message.messageType == .text {
// //
// let word = GameDataManager.shared.matchKeywordWith(sourceStr: message.text?.lowercased() ?? "")
// if word.count > 0 {
// dict["keyword"] = word
// dict["type"] = "NOTICE_KEYWORD"
// message.remoteExt = dict
// return false
// }
// }
return true
}
}