| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- //
- // LNGiftManager.swift
- // Gami
- //
- // Created by OneeChan on 2026/3/24.
- //
- import Foundation
- private struct LNGiftCacheList: Codable {
- var list: [LNGiftResource]
- var version: String
- }
- class LNGiftManager {
- static let shared = LNGiftManager()
-
- private let pageSize = 100
- private let cacheURL = URL.cacheDir
- .appendingPathComponent("Gift", isDirectory: true)
- .appendingPathComponent("gift_resources.json")
-
- private var resourceMap: [String: LNGiftResource] = [:]
- private var resourceVersion: String = ""
-
- private var isRefreshing = false
-
- private init() {
- DispatchQueue.global().async { [weak self] in
- guard let self else { return }
- loadCache()
- }
- LNEventDeliver.addObserver(self)
- }
-
- func resource(for id: String) -> LNGiftResource? {
- resourceMap[id]
- }
-
- func fetchGiftList(roomId: String, queue: DispatchQueue = .main, handler: @escaping ([LNGiftItemVO]?) -> Void) {
- LNHttpManager.shared.loadGiftList(roomId: roomId) { [weak self] res, err in
- guard let self else { return }
- let list = res?.list.filter { self.resourceMap[$0.resId] != nil }
- queue.asyncIfNotGlobal {
- handler(list)
- }
- if let list, list.count != res?.list.count {
- updateGiftResource()
- }
- if let err {
- showToast(err.errorDesc)
- }
- }
- }
- }
- extension LNGiftManager {
- func updateGiftResource() {
- guard !isRefreshing else { return }
-
- var changedGifts: [LNGiftResource] = []
- var newVersion = ""
- isRefreshing = true
-
- func _fetchResource(next: String) {
- LNHttpManager.shared.loadResourceList(version: resourceVersion, size: pageSize, next: next) { [weak self] res, err in
- guard let self else { return }
- guard let res else {
- isRefreshing = false
- return
- }
- if !res.list.isEmpty {
- changedGifts.append(contentsOf: res.list)
- }
- if !res.version.isEmpty {
- newVersion = res.version
- }
- if res.list.isEmpty || res.next.isEmpty != false {
- mergeChangedResources(changedGifts, version: newVersion)
- isRefreshing = false
- } else {
- _fetchResource(next: next)
- }
- }
- }
- _fetchResource(next: "")
- }
-
- private func mergeChangedResources(_ changedList: [LNGiftResource], version: String) {
- guard !changedList.isEmpty, !version.isEmpty else { return }
-
- var newMap = resourceMap
- changedList.forEach { item in
- guard !item.id.isEmpty else { return }
- newMap[item.id] = item
- }
-
- resourceMap = newMap
- resourceVersion = version
-
- saveCache()
- }
- }
-
- extension LNGiftManager {
- private func loadCache() {
- guard let data = try? Data(contentsOf: cacheURL) else { return }
-
- do {
- let snapshot = try JSONDecoder().decode(LNGiftCacheList.self, from: data)
- resourceVersion = snapshot.version
- resourceMap = snapshot.list.reduce(into: [:]) { partialResult, item in
- guard !item.id.isEmpty else { return }
- partialResult[item.id] = item
- }
- } catch {
- Log.e("load gift resource cache failed: \(error.localizedDescription)")
- }
- }
-
- private func saveCache() {
- let snapshot = LNGiftCacheList(
- list: Array(resourceMap.values),
- version: resourceVersion
- )
-
- do {
- let folderURL = cacheURL.deletingLastPathComponent()
- try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
- let data = try JSONEncoder().encode(snapshot)
- try data.write(to: cacheURL, options: .atomic)
- } catch {
- Log.e("save gift resource cache failed: \(error.localizedDescription)")
- }
- }
- }
- extension LNGiftManager {
- func sendGift(params: LNSendGiftParams, queue: DispatchQueue = .main,
- handler: @escaping (Bool) -> Void) {
- let remain: TimeInterval = LNUserDefaults[.remainExchange, 0]
- if remain.isSameDay {
- params.seamlessRedeem = true
- }
-
- LNHttpManager.shared.sendGift(params: params) { res, err in
- queue.asyncIfNotGlobal {
- handler(err == nil)
- }
- if let err {
- showToast(err.errorDesc)
- if case .serverError(let code, _) = err {
- runOnMain {
- if code == LNOrderErrorCode.NotEnoughMoney.rawValue {
- let panel = LNMoneyNotEnoughAlertView()
- panel.update(.diamond)
- panel.popup()
- } else if LNOrderErrorCode.NotEnoughMoneyButCanExchange.rawValue == code {
- let panel = LNMoneyNotEnoughAlertView()
- panel.update(.diamond, exchange: .coin)
- panel.exchangeHandler = {
- params.seamlessRedeem = true
- self.sendGift(params: params, queue: queue, handler: handler)
- }
- panel.popup()
- }
- }
- }
- }
- if let res {
- LNPurchaseManager.shared.updateWallet(diamond: res.diamond, coin: res.goldcoin)
- }
- }
- }
- }
- extension LNGiftManager: LNAccountManagerNotify {
- func onUserLogin() {
- updateGiftResource()
- }
- }
|