LNProfileManager.swift 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. //
  2. // LNProfileManager.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/11/6.
  6. //
  7. import Foundation
  8. protocol LNProfileManagerNotify {
  9. func onUserInfoChanged(userInfo: LNUserProfileVO)
  10. func onBindPhoneCaptchaCoolDownChanged(time: Int)
  11. func onUserVoiceBarInfoChanged()
  12. }
  13. extension LNProfileManagerNotify {
  14. func onUserInfoChanged(userInfo: LNUserProfileVO) { }
  15. func onBindPhoneCaptchaCoolDownChanged(time: Int) { }
  16. func onUserVoiceBarInfoChanged() { }
  17. }
  18. var myUserInfo: LNUserProfileVO {
  19. LNProfileManager.shared.myUserInfo
  20. }
  21. var myVoiceBarInfo: LNUserVoiceStateVO {
  22. LNProfileManager.shared.myVoiceState
  23. }
  24. class LNProfileManager {
  25. static let shared = LNProfileManager()
  26. static let nameMaxInput = 25
  27. static let bioMaxInput = 100
  28. fileprivate var myUserInfo: LNUserProfileVO = LNUserProfileVO()
  29. fileprivate var myVoiceState: LNUserVoiceStateVO = LNUserVoiceStateVO()
  30. private var randomProfile: LNRandomProfileResponse?
  31. private let captchaCoolDown = 60
  32. private var captchaRemain = 0
  33. private var captchaTimer: Timer?
  34. var canSendCaptcha: Bool {
  35. captchaRemain == 0
  36. }
  37. private init() {
  38. LNEventDeliver.addObserver(self)
  39. }
  40. }
  41. extension LNProfileManager {
  42. func reloadMyProfile() {
  43. LNHttpManager.shared.getMyProfile { [weak self] res, err in
  44. guard let self else { return }
  45. guard err == nil, let res else { return }
  46. if let relation = res.userFollowStat {
  47. LNRelationManager.shared.updateMyRelationInfo(relation)
  48. }
  49. if let voice = res.userVoice {
  50. myVoiceState = voice
  51. notifyUserVoiceBarInfoChanged()
  52. }
  53. }
  54. getUserProfile(uid: myUid) { _ in }
  55. }
  56. func modifyMyProfile(config: LNProfileUpdateConfig, queue: DispatchQueue = .main,
  57. handler: @escaping (Bool) -> Void) {
  58. LNHttpManager.shared.modifyMyProfile(
  59. config: config) { [weak self] err in
  60. guard let self else { return }
  61. if let err {
  62. showToast(err.errorDesc)
  63. } else {
  64. reloadMyProfile()
  65. if config.interest != nil {
  66. LNGameMateManager.shared.getGameTypeList { _ in }
  67. }
  68. }
  69. queue.asyncIfNotGlobal {
  70. handler(err == nil)
  71. }
  72. }
  73. }
  74. func setMyVoiceBar(url: String, duration: Int, queue: DispatchQueue = .main,
  75. handler: @escaping (Bool) -> Void) {
  76. LNHttpManager.shared.setMyVoiceBar(url: url, duration: duration) { [weak self] err in
  77. queue.asyncIfNotGlobal {
  78. handler(err == nil)
  79. }
  80. if let err {
  81. showToast(err.errorDesc)
  82. } else {
  83. guard let self else { return }
  84. reloadMyProfile()
  85. }
  86. }
  87. }
  88. func cleanVoiceBar(queue: DispatchQueue = .main, handler: @escaping (Bool) -> Void) {
  89. LNHttpManager.shared.cleanVoiceBar { [weak self] err in
  90. queue.asyncIfNotGlobal {
  91. handler(err == nil)
  92. }
  93. if let err {
  94. showToast(err.errorDesc)
  95. } else {
  96. guard let self else { return }
  97. reloadMyProfile()
  98. }
  99. }
  100. }
  101. }
  102. extension LNProfileManager {
  103. func getUserProfile(uid: String, queue: DispatchQueue = .main, handler: @escaping (LNUserProfileVO?) -> Void) {
  104. LNHttpManager.shared.getUsersInfo(uids: [uid]) { [weak self] res, err in
  105. guard let self else { return }
  106. if let res, err == nil {
  107. res.list.forEach {
  108. if $0.userNo.isMyUid {
  109. self.myUserInfo = $0
  110. }
  111. self.notifyUserInfoChanged(newInfo: $0)
  112. }
  113. } else {
  114. showToast(err?.errorDesc)
  115. }
  116. queue.asyncIfNotGlobal {
  117. handler(res?.list.first)
  118. }
  119. }
  120. }
  121. func getRandomProfile(queue: DispatchQueue = .main,
  122. handler: @escaping (LNProfileRandomInfoVO?, LNProfileRandomInfoVO?) -> Void) {
  123. if let randomProfile {
  124. handler(randomProfile.male, randomProfile.female)
  125. return
  126. }
  127. LNHttpManager.shared.getRandomProfile { [weak self] res, err in
  128. if let self, let res {
  129. randomProfile = res
  130. }
  131. queue.asyncIfNotGlobal {
  132. handler(res?.male, res?.female)
  133. }
  134. }
  135. }
  136. // func getUserOnlineState(uids: [String], queue: DispatchQueue = .main, handler: @escaping ([String: Bool]) -> Void) {
  137. // LNHttpManager.shared.getUserOnlineState(uids: uids) { res, err in
  138. // queue.asyncIfNotGlobal {
  139. // handler(res?.list.reduce(into: [String: Bool](), { partialResult, state in
  140. // partialResult[state.userNo] = state.online
  141. // }) ?? [:])
  142. // }
  143. // }
  144. // }
  145. }
  146. extension LNProfileManager {
  147. func getBindPhoneCaptcha(code: String, phone: String,
  148. queue: DispatchQueue = .main,
  149. handler: @escaping (Bool) -> Void) {
  150. LNHttpManager.shared.getBindPhoneCaptcha(code: code, phone: phone) { [weak self] err in
  151. queue.asyncIfNotGlobal {
  152. handler(err == nil)
  153. }
  154. guard let self else { return }
  155. if let err {
  156. showToast(err.errorDesc)
  157. } else {
  158. captchaRemain = captchaCoolDown
  159. notifyCaptchaTime(time: captchaRemain)
  160. startCaptchaTimer()
  161. }
  162. }
  163. }
  164. func bindPhone(code: String, phone: String,
  165. queue: DispatchQueue = .main,
  166. captcha: String, handler: @escaping (Bool) -> Void) {
  167. LNHttpManager.shared.bindPhone(code: code, phone: phone, captcha: captcha) { err in
  168. queue.asyncIfNotGlobal {
  169. handler(err == nil)
  170. }
  171. if let err {
  172. showToast(err.errorDesc)
  173. }
  174. }
  175. }
  176. private func startCaptchaTimer() {
  177. DispatchQueue.main.async { [weak self] in
  178. guard let self else { return }
  179. stopCaptchaTimer()
  180. let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
  181. guard let self else { return }
  182. captchaRemain -= 1
  183. notifyCaptchaTime(time: captchaRemain)
  184. if captchaRemain == 0 {
  185. stopCaptchaTimer()
  186. }
  187. }
  188. RunLoop.main.add(timer, forMode: .common)
  189. captchaTimer = timer
  190. }
  191. }
  192. private func stopCaptchaTimer() {
  193. captchaTimer?.invalidate()
  194. captchaTimer = nil
  195. }
  196. }
  197. extension LNProfileManager {
  198. func reportCurrentLanguage(code: String,
  199. queue: DispatchQueue = .main,
  200. handler: @escaping (Bool) -> Void) {
  201. LNHttpManager.shared.reportCurrentLanguage(code: code) { err in
  202. queue.asyncIfNotGlobal {
  203. handler(err == nil)
  204. }
  205. }
  206. }
  207. }
  208. extension LNProfileManager: LNAccountManagerNotify {
  209. func onUserLogin() {
  210. reloadMyProfile()
  211. }
  212. func onUserLogout() {
  213. myUserInfo = LNUserProfileVO()
  214. }
  215. }
  216. extension LNProfileManager {
  217. private func notifyUserInfoChanged(newInfo: LNUserProfileVO) {
  218. LNEventDeliver.notifyEvent { ($0 as? LNProfileManagerNotify)?.onUserInfoChanged(userInfo: newInfo) }
  219. }
  220. private func notifyCaptchaTime(time: Int) {
  221. LNEventDeliver.notifyEvent {
  222. ($0 as? LNProfileManagerNotify)?.onBindPhoneCaptchaCoolDownChanged(time: time)
  223. }
  224. }
  225. private func notifyUserVoiceBarInfoChanged() {
  226. LNEventDeliver.notifyEvent { ($0 as? LNProfileManagerNotify)?.onUserVoiceBarInfoChanged() }
  227. }
  228. }