MessageSettingsViewModel.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. //
  2. // MessageSettingsViewModel.swift
  3. // Bark
  4. //
  5. // Created by huangfeng on 2020/11/20.
  6. // Copyright © 2020 Fin. All rights reserved.
  7. //
  8. import Foundation
  9. import Material
  10. import RealmSwift
  11. import RxCocoa
  12. import RxDataSources
  13. import RxSwift
  14. import SwiftyJSON
  15. class MessageSettingsViewModel: ViewModel, ViewModelType {
  16. struct Input {
  17. var itemSelected: Driver<MessageSettingItem>
  18. var deviceToken: Driver<String?>
  19. var backupAction: Driver<Void>
  20. var restoreAction: Driver<Data>
  21. var viewDidAppear: Observable<Void>
  22. var archiveSettingRelay: BehaviorRelay<Bool>
  23. }
  24. struct Output {
  25. var settings: Driver<[SectionModel<String, MessageSettingItem>]>
  26. var openUrl: Driver<URL>
  27. var copyDeviceToken: Driver<String>
  28. var exportData: Driver<Data>
  29. }
  30. func transform(input: Input) -> Output {
  31. let restoreSuccess = input
  32. .restoreAction
  33. .compactMap { data -> Void? in
  34. guard let json = try? JSON(data: data), let arr = json.array else {
  35. return nil
  36. }
  37. guard let realm = try? Realm() else {
  38. return nil
  39. }
  40. try? realm.write {
  41. for message in arr {
  42. guard let id = message["id"].string else {
  43. continue
  44. }
  45. guard let createDate = message["createDate"].int64 else {
  46. continue
  47. }
  48. let title = message["title"].string
  49. let body = message["body"].string
  50. let url = message["url"].string
  51. let group = message["group"].string
  52. let messageObject = Message()
  53. messageObject.id = id
  54. messageObject.title = title
  55. messageObject.body = body
  56. messageObject.url = url
  57. messageObject.group = group
  58. messageObject.createDate = Date(timeIntervalSince1970: TimeInterval(createDate))
  59. realm.add(messageObject, update: .modified)
  60. }
  61. }
  62. return ()
  63. }.asObservable().share()
  64. let settings: [MessageSettingItem] = {
  65. var settings = [MessageSettingItem]()
  66. // settings.append(.label(text: "iCloud"))
  67. // settings.append(.iCloudStatus)
  68. // settings.append(.label(text: NSLocalizedString("iCloudSync")))
  69. settings.append(.label(text: NSLocalizedString("historyMessage")))
  70. settings.append(.backup(viewModel: MutableTextCellViewModel(
  71. title: "\(NSLocalizedString("export"))/\(NSLocalizedString("import"))",
  72. text: Observable.merge([restoreSuccess, input.viewDidAppear])
  73. .map { _ in
  74. if let realm = try? Realm() {
  75. return realm.objects(Message.self)
  76. .filter("isDeleted != true")
  77. .count
  78. }
  79. return 0
  80. }
  81. .map { count in
  82. "\(count) \(NSLocalizedString("items"))"
  83. }
  84. .asDriver(onErrorDriveWith: .empty()))
  85. ))
  86. settings.append(.label(text: NSLocalizedString("exportOrImport")))
  87. settings.append(.archiveSetting(viewModel: ArchiveSettingCellViewModel(on: input.archiveSettingRelay)))
  88. settings.append(.label(text: NSLocalizedString("archiveNote")))
  89. settings.append(.label(text: NSLocalizedString("info")))
  90. settings.append(.deviceToken(
  91. viewModel: MutableTextCellViewModel(
  92. title: "Device Token",
  93. text: input
  94. .deviceToken
  95. .map {
  96. deviceToken in
  97. if let deviceToken = deviceToken {
  98. return "\(deviceToken.prefix(2))****\(deviceToken.suffix(4))"
  99. }
  100. return NSLocalizedString("unknown")
  101. })
  102. ))
  103. settings.append(.label(text: NSLocalizedString("deviceTokenInfo")))
  104. if let infoDict = Bundle.main.infoDictionary,
  105. let runId = infoDict["GitHub Run Id"] as? String
  106. {
  107. settings.append(.detail(
  108. title: "Github Run Id",
  109. text: "\(runId)",
  110. textColor: BKColor.grey.darken2,
  111. url: URL(string: "https://github.com/Finb/Bark/actions/runs/\(runId)")))
  112. settings.append(.label(text: NSLocalizedString("buildDesc")))
  113. }
  114. settings.append(.label(text: NSLocalizedString("other")))
  115. settings.append(.detail(
  116. title: NSLocalizedString("faq"),
  117. text: nil,
  118. textColor: nil,
  119. url: URL(string: NSLocalizedString("faqUrl"))))
  120. settings.append(.spacer(height: 0.5, color: BKColor.grey.lighten4))
  121. settings.append(.detail(
  122. title: NSLocalizedString("documentation"),
  123. text: nil,
  124. textColor: nil,
  125. url: URL(string: NSLocalizedString("docUrl"))))
  126. return settings
  127. }()
  128. let openUrl = input.itemSelected.compactMap { item -> URL? in
  129. if case let MessageSettingItem.detail(_, _, _, url) = item {
  130. return url
  131. }
  132. return nil
  133. }
  134. let deviceTokenValue: BehaviorRelay<String?> = BehaviorRelay(value: nil)
  135. input.deviceToken.drive(deviceTokenValue)
  136. .disposed(by: rx.disposeBag)
  137. let copyDeviceToken = input.itemSelected.compactMap { item -> String? in
  138. if case MessageSettingItem.deviceToken = item {
  139. return deviceTokenValue.value
  140. }
  141. return nil
  142. }
  143. // 导出数据
  144. let exportSuccess = input.backupAction
  145. .asObservable()
  146. .subscribe(on: ConcurrentDispatchQueueScheduler(qos: .userInitiated))
  147. .compactMap { _ in
  148. if let realm = try? Realm() {
  149. let messages = realm.objects(Message.self)
  150. .filter("isDeleted != true")
  151. .sorted(byKeyPath: "createDate", ascending: false)
  152. var arr = [[String: AnyObject]]()
  153. for message in messages {
  154. arr.append(message.toDictionary())
  155. }
  156. return try? JSON(arr).rawData(options: JSONSerialization.WritingOptions.prettyPrinted)
  157. }
  158. return nil
  159. }
  160. return Output(
  161. settings: Driver<[SectionModel<String, MessageSettingItem>]>
  162. .just([SectionModel(model: "model", items: settings)]),
  163. openUrl: openUrl,
  164. copyDeviceToken: copyDeviceToken,
  165. exportData: exportSuccess.asDriver(onErrorDriveWith: .empty()))
  166. }
  167. }
  168. enum MessageSettingItem {
  169. // 普通标题标签
  170. case label(text: String)
  171. // iCloud 状态
  172. case iCloudStatus
  173. // 默认保存
  174. case archiveSetting(viewModel: ArchiveSettingCellViewModel)
  175. // 带 详细按钮的 文本cell
  176. case detail(title: String?, text: String?, textColor: UIColor?, url: URL?)
  177. // 备份还原按钮
  178. case backup(viewModel: MutableTextCellViewModel)
  179. // deviceToken
  180. case deviceToken(viewModel: MutableTextCellViewModel)
  181. // 分隔线
  182. case spacer(height: CGFloat, color: UIColor?)
  183. }