LNGiftManager.swift 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. //
  2. // LNGiftManager.swift
  3. // Gami
  4. //
  5. // Created by OneeChan on 2026/3/24.
  6. //
  7. import Foundation
  8. private struct LNGiftCacheList: Codable {
  9. var list: [LNGiftResource]
  10. var version: String
  11. }
  12. class LNGiftManager {
  13. static let shared = LNGiftManager()
  14. private let pageSize = 100
  15. private let cacheURL = URL.cacheDir
  16. .appendingPathComponent("Gift", isDirectory: true)
  17. .appendingPathComponent("gift_resources.json")
  18. private var resourceMap: [String: LNGiftResource] = [:]
  19. private var resourceVersion: String = ""
  20. private var isRefreshing = false
  21. private init() {
  22. DispatchQueue.global().async { [weak self] in
  23. guard let self else { return }
  24. loadCache()
  25. }
  26. LNEventDeliver.addObserver(self)
  27. }
  28. func resource(for id: String) -> LNGiftResource? {
  29. resourceMap[id]
  30. }
  31. func fetchGiftList(roomId: String, queue: DispatchQueue = .main, handler: @escaping ([LNGiftItemVO]?) -> Void) {
  32. LNHttpManager.shared.loadGiftList(roomId: roomId) { [weak self] res, err in
  33. guard let self else { return }
  34. let list = res?.list.filter { self.resourceMap[$0.resId] != nil }
  35. queue.asyncIfNotGlobal {
  36. handler(list)
  37. }
  38. if let list, list.count != res?.list.count {
  39. updateGiftResource()
  40. }
  41. if let err {
  42. showToast(err.errorDesc)
  43. }
  44. }
  45. }
  46. }
  47. extension LNGiftManager {
  48. func updateGiftResource() {
  49. guard !isRefreshing else { return }
  50. var changedGifts: [LNGiftResource] = []
  51. var newVersion = ""
  52. isRefreshing = true
  53. func _fetchResource(next: String) {
  54. LNHttpManager.shared.loadResourceList(version: resourceVersion, size: pageSize, next: next) { [weak self] res, err in
  55. guard let self else { return }
  56. guard let res else {
  57. isRefreshing = false
  58. return
  59. }
  60. if !res.list.isEmpty {
  61. changedGifts.append(contentsOf: res.list)
  62. }
  63. if !res.version.isEmpty {
  64. newVersion = res.version
  65. }
  66. if res.list.isEmpty || res.next.isEmpty != false {
  67. mergeChangedResources(changedGifts, version: newVersion)
  68. isRefreshing = false
  69. } else {
  70. _fetchResource(next: next)
  71. }
  72. }
  73. }
  74. _fetchResource(next: "")
  75. }
  76. private func mergeChangedResources(_ changedList: [LNGiftResource], version: String) {
  77. guard !changedList.isEmpty, !version.isEmpty else { return }
  78. var newMap = resourceMap
  79. changedList.forEach { item in
  80. guard !item.id.isEmpty else { return }
  81. newMap[item.id] = item
  82. }
  83. resourceMap = newMap
  84. resourceVersion = version
  85. saveCache()
  86. }
  87. }
  88. extension LNGiftManager {
  89. private func loadCache() {
  90. guard let data = try? Data(contentsOf: cacheURL) else { return }
  91. do {
  92. let snapshot = try JSONDecoder().decode(LNGiftCacheList.self, from: data)
  93. resourceVersion = snapshot.version
  94. resourceMap = snapshot.list.reduce(into: [:]) { partialResult, item in
  95. guard !item.id.isEmpty else { return }
  96. partialResult[item.id] = item
  97. }
  98. } catch {
  99. Log.e("load gift resource cache failed: \(error.localizedDescription)")
  100. }
  101. }
  102. private func saveCache() {
  103. let snapshot = LNGiftCacheList(
  104. list: Array(resourceMap.values),
  105. version: resourceVersion
  106. )
  107. do {
  108. let folderURL = cacheURL.deletingLastPathComponent()
  109. try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
  110. let data = try JSONEncoder().encode(snapshot)
  111. try data.write(to: cacheURL, options: .atomic)
  112. } catch {
  113. Log.e("save gift resource cache failed: \(error.localizedDescription)")
  114. }
  115. }
  116. }
  117. extension LNGiftManager {
  118. func sendGift(params: LNSendGiftParams, queue: DispatchQueue = .main,
  119. handler: @escaping (Bool) -> Void) {
  120. let remain: TimeInterval = LNUserDefaults[.remainExchange, 0]
  121. if remain.isSameDay {
  122. params.seamlessRedeem = true
  123. }
  124. LNHttpManager.shared.sendGift(params: params) { res, err in
  125. queue.asyncIfNotGlobal {
  126. handler(err == nil)
  127. }
  128. if let err {
  129. showToast(err.errorDesc)
  130. if case .serverError(let code, _) = err {
  131. runOnMain {
  132. if code == LNOrderErrorCode.NotEnoughMoney.rawValue {
  133. let panel = LNMoneyNotEnoughAlertView()
  134. panel.update(.diamond)
  135. panel.popup()
  136. } else if LNOrderErrorCode.NotEnoughMoneyButCanExchange.rawValue == code {
  137. let panel = LNMoneyNotEnoughAlertView()
  138. panel.update(.diamond, exchange: .coin)
  139. panel.exchangeHandler = {
  140. params.seamlessRedeem = true
  141. self.sendGift(params: params, queue: queue, handler: handler)
  142. }
  143. panel.popup()
  144. }
  145. }
  146. }
  147. }
  148. if let res {
  149. LNPurchaseManager.shared.updateWallet(diamond: res.diamond, coin: res.goldcoin)
  150. }
  151. }
  152. }
  153. }
  154. extension LNGiftManager: LNAccountManagerNotify {
  155. func onUserLogin() {
  156. updateGiftResource()
  157. }
  158. }