HomeViewModel.swift 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. //
  2. // HomeViewModel.swift
  3. // Bark
  4. //
  5. // Created by huangfeng on 2020/11/18.
  6. // Copyright © 2020 Fin. All rights reserved.
  7. //
  8. import Foundation
  9. import RxCocoa
  10. import RxDataSources
  11. import RxSwift
  12. import SwiftyJSON
  13. import UserNotifications
  14. class HomeViewModel: ViewModel, ViewModelType {
  15. struct Input {
  16. let addCustomServerTap: Driver<Void>
  17. let serverListTap: Driver<Void>
  18. let viewDidAppear: Driver<Void>
  19. let start: Driver<Void>
  20. let clientState: Driver<Client.ClienState>
  21. let authorizationStatus: Single<UNAuthorizationStatus>
  22. let startRequestAuthorizationCreator: () -> Observable<Bool>
  23. }
  24. struct Output {
  25. let previews: Driver<[SectionModel<String, PreviewCardCellViewModel>]>
  26. let push: Driver<ViewModel>
  27. let present: Driver<ViewModel>
  28. let title: Driver<String>
  29. let clienStateChanged: Driver<Client.ClienState>
  30. let tableViewHidden: Driver<Bool>
  31. let showSnackbar: Driver<String>
  32. let startButtonEnable: Driver<Bool>
  33. let copy: Driver<String>
  34. let preview: Driver<URL>
  35. let reloadData: Driver<Void>
  36. let registerForRemoteNotifications: Driver<Void>
  37. }
  38. let previews: [PreviewModel] = [
  39. PreviewModel(
  40. body: NSLocalizedString("CustomedNotificationContent"),
  41. notice: NSLocalizedString("Notice1")
  42. ),
  43. PreviewModel(
  44. title: NSLocalizedString("CustomedNotificationTitle"),
  45. body: NSLocalizedString("CustomedNotificationContent"),
  46. notice: NSLocalizedString("Notice2")
  47. ),
  48. PreviewModel(
  49. body: NSLocalizedString("notificationSound"),
  50. notice: NSLocalizedString("setSounds"),
  51. queryParameter: "sound=minuet",
  52. moreInfo: NSLocalizedString("viewAllSounds"),
  53. moreViewModel: SoundsViewModel()
  54. ),
  55. PreviewModel(
  56. body: NSLocalizedString("ringtone"),
  57. notice: NSLocalizedString("ringtoneNotice"),
  58. queryParameter: "call=1"
  59. ),
  60. PreviewModel(
  61. body: NSLocalizedString("archiveNotificationMessageTitle"),
  62. notice: NSLocalizedString("archiveNotificationMessage"),
  63. queryParameter: "isArchive=1"
  64. ),
  65. PreviewModel(
  66. body: NSLocalizedString("notificationIcon"),
  67. notice: NSLocalizedString("notificationIconNotice"),
  68. queryParameter: "icon=https://day.app/assets/images/avatar.jpg",
  69. image: UIImage(named: "icon")
  70. ),
  71. PreviewModel(
  72. body: NSLocalizedString("messageGroup"),
  73. notice: NSLocalizedString("groupMessagesNotice"),
  74. queryParameter: "group=groupName",
  75. image: UIImage(named: "group")
  76. ),
  77. PreviewModel(
  78. body: NSLocalizedString("pushNotificationEncryption"),
  79. notice: NSLocalizedString("encryptionNotice"),
  80. queryParameter: "ciphertext=ciphertext",
  81. moreInfo: NSLocalizedString("encryptionSettings"),
  82. moreViewModel: CryptoSettingViewModel()
  83. ),
  84. PreviewModel(
  85. body: NSLocalizedString("interruptionLevel"),
  86. notice: NSLocalizedString("interruptionLevelNotice"),
  87. queryParameter: "level=timeSensitive"
  88. ),
  89. PreviewModel(
  90. body: "URL Test",
  91. notice: NSLocalizedString("urlParameter"),
  92. queryParameter: "url=https://www.baidu.com"
  93. ),
  94. PreviewModel(
  95. body: "Copy Test",
  96. notice: NSLocalizedString("copyParameter"),
  97. queryParameter: "copy=test",
  98. image: UIImage(named: "copyTest")
  99. ),
  100. PreviewModel(
  101. body: NSLocalizedString("badge"),
  102. notice: NSLocalizedString("badgeNotice"),
  103. queryParameter: "badge=1"
  104. ),
  105. PreviewModel(
  106. body: NSLocalizedString("automaticallyCopyTitle"),
  107. notice: NSLocalizedString("automaticallyCopy"),
  108. queryParameter: "autoCopy=1&copy=optional"
  109. )
  110. ]
  111. func transform(input: Input) -> Output {
  112. let title = BehaviorRelay(value: URL(string: ServerManager.shared.currentServer.address)?.host ?? "")
  113. let sectionModel = SectionModel(
  114. model: "previews",
  115. items: previews.map { PreviewCardCellViewModel(previewModel: $0, clientState: input.clientState) }
  116. )
  117. // 点击跳转到添加自定义服务器
  118. let customServer = input.addCustomServerTap.map { NewServerViewModel() as ViewModel }
  119. // 如果更改了服务器地址,返回时也需更改 title
  120. customServer
  121. .flatMapLatest { model -> Driver<String> in
  122. (model as! NewServerViewModel).pop.asDriver(onErrorJustReturn: "")
  123. }
  124. .drive(title)
  125. .disposed(by: rx.disposeBag)
  126. // 点击preview中的notice ,跳转到对应的页面
  127. let noticeTap = Driver.merge(sectionModel.items.map { $0.noticeTap.asDriver(onErrorDriveWith: .empty()) })
  128. // 判断服务器状态
  129. let clienState = input.viewDidAppear
  130. .asObservable().flatMapLatest { _ -> Observable<Result<JSON, ApiError>> in
  131. BarkApi.provider
  132. .request(.ping(baseURL: ServerManager.shared.currentServer.address))
  133. .filterResponseError()
  134. }
  135. .map { response -> Client.ClienState in
  136. switch response {
  137. case .failure:
  138. return .serverError
  139. default:
  140. return .ok
  141. }
  142. }
  143. // 根据通知权限,设置是否隐藏注册按钮、显示示例预览列表
  144. let tableViewHidden = input.authorizationStatus.map { $0 == .authorized }
  145. .asObservable()
  146. .concat(
  147. input.start.asObservable().flatMapLatest { input.startRequestAuthorizationCreator() }
  148. )
  149. .asDriver(onErrorJustReturn: false)
  150. let showSnackbar = PublishRelay<String>()
  151. // 点击注册按钮后,如果不允许推送,弹出提示
  152. tableViewHidden
  153. .skip(1)
  154. .compactMap { granted -> String? in
  155. if !granted {
  156. return NSLocalizedString("AllowNotifications")
  157. }
  158. return nil
  159. }
  160. .asObservable()
  161. .bind(to: showSnackbar)
  162. .disposed(by: rx.disposeBag)
  163. // 点击注册按钮,如果用户允许推送,则通知 viewController 注册推送
  164. let registerForRemoteNotifications = tableViewHidden
  165. .skip(1)
  166. .filter { $0 }
  167. .map { _ in () }
  168. // client state 变化时,发出相应错误提醒
  169. input.clientState.drive(onNext: { state in
  170. switch state {
  171. case .ok: break
  172. case .serverError:
  173. showSnackbar.accept(NSLocalizedString("ServerError"))
  174. default: break
  175. }
  176. })
  177. .disposed(by: rx.disposeBag)
  178. let serverList = input.serverListTap.map { ServerListViewModel() as ViewModel }
  179. // 服务器发生了改变
  180. serverList
  181. .flatMapLatest { model -> Driver<Server> in
  182. (model as! ServerListViewModel).currentServerChanged.asDriver(onErrorDriveWith: .empty())
  183. }
  184. .map { server -> String in
  185. (try? server.address.asURL().host) ?? ""
  186. }
  187. .drive(title)
  188. .disposed(by: rx.disposeBag)
  189. return Output(
  190. previews: Driver.just([sectionModel]),
  191. push: Driver<ViewModel>.merge(customServer, noticeTap),
  192. present: serverList.asDriver(),
  193. title: title.asDriver(),
  194. clienStateChanged: clienState.asDriver(onErrorDriveWith: .empty()),
  195. tableViewHidden: tableViewHidden,
  196. showSnackbar: showSnackbar.asDriver(onErrorDriveWith: .empty()),
  197. startButtonEnable: Driver.just(true),
  198. copy: Driver.merge(sectionModel.items.map { $0.copy.asDriver(onErrorDriveWith: .empty()) }),
  199. preview: Driver.merge(sectionModel.items.map { $0.preview.asDriver(onErrorDriveWith: .empty()) }),
  200. reloadData: input.clientState.map { _ in () },
  201. registerForRemoteNotifications: registerForRemoteNotifications
  202. )
  203. }
  204. }