MessageListViewModel.swift 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. //
  2. // MessageListViewModel.swift
  3. // Bark
  4. //
  5. // Created by huangfeng on 2020/11/21.
  6. // Copyright © 2020 Fin. All rights reserved.
  7. //
  8. import Foundation
  9. import RealmSwift
  10. import RxCocoa
  11. import RxDataSources
  12. import RxSwift
  13. class MessageListViewModel: ViewModel, ViewModelType {
  14. struct Input {
  15. var refresh: Driver<Void>
  16. var loadMore: Driver<Void>
  17. var itemDelete: Driver<Int>
  18. var itemSelected: Driver<Int>
  19. var delete: Driver<MessageDeleteType>
  20. var groupTap: Driver<Void>
  21. var searchText: Observable<String?>
  22. }
  23. struct Output {
  24. var messages: Driver<[MessageSection]>
  25. var refreshAction: Driver<MJRefreshAction>
  26. var alertMessage: Driver<(String, Int)>
  27. var groupFilter: Driver<GroupFilterViewModel>
  28. var title: Driver<String>
  29. }
  30. private var results: Results<Message>?
  31. // 根据群组获取消息
  32. private func getResults(filterGroups: [String?], searchText: String?) -> Results<Message>? {
  33. if let realm = try? Realm() {
  34. var results = realm.objects(Message.self)
  35. .filter("isDeleted != true")
  36. .sorted(byKeyPath: "createDate", ascending: false)
  37. if filterGroups.count > 0 {
  38. results = results.filter("group in %@", filterGroups)
  39. }
  40. if let text = searchText, text.count > 0 {
  41. results = results.filter("title contains[c] '\(text)' or body contains[c] '\(text)'")
  42. }
  43. return results
  44. }
  45. return nil
  46. }
  47. private var page = 0
  48. private let pageCount = 20
  49. private func getNextPage() -> [Message] {
  50. if let result = results {
  51. let startIndex = page * pageCount
  52. let endIndex = min(startIndex + pageCount, result.count)
  53. guard endIndex > startIndex else {
  54. return []
  55. }
  56. var messages: [Message] = []
  57. for i in startIndex..<endIndex {
  58. messages.append(result[i])
  59. }
  60. page += 1
  61. return messages
  62. }
  63. return []
  64. }
  65. func transform(input: Input) -> Output {
  66. let alertMessage = input.itemSelected.map { [weak self] index in
  67. guard let results = self?.results else {
  68. return ("", 0)
  69. }
  70. let message = results[index]
  71. // let message = model.message
  72. var copyContent: String = ""
  73. if let title = message.title {
  74. copyContent += "\(title)\n"
  75. }
  76. if let body = message.body {
  77. copyContent += "\(body)\n"
  78. }
  79. if let url = message.url {
  80. copyContent += "\(url)\n"
  81. }
  82. copyContent = String(copyContent.prefix(copyContent.count - 1))
  83. return (copyContent, index)
  84. }
  85. // 标题
  86. let titleRelay = BehaviorRelay<String>(value: NSLocalizedString("historyMessage"))
  87. // 数据源
  88. let messagesRelay = BehaviorRelay<[MessageSection]>(value: [])
  89. // 刷新操作
  90. let refreshAction = BehaviorRelay<MJRefreshAction>(value: .none)
  91. // 切换群组
  92. let filterGroups: BehaviorRelay<[String?]> = {
  93. if let groups: [String?] = Settings["me.fin.filterGroups"] {
  94. return BehaviorRelay<[String?]>(value: groups)
  95. }
  96. return BehaviorRelay<[String?]>(value: [])
  97. }()
  98. // Message 转 MessageSection
  99. func messagesToMessageSection(messages: [Message]) -> [MessageSection] {
  100. let cellViewModels = messages.map { message -> MessageTableViewCellViewModel in
  101. MessageTableViewCellViewModel(message: message)
  102. }
  103. return [MessageSection(header: "model", messages: cellViewModels)]
  104. }
  105. // 切换分组时,更新分组名
  106. filterGroups
  107. .subscribe(onNext: { filterGroups in
  108. if filterGroups.count <= 0 {
  109. titleRelay.accept(NSLocalizedString("historyMessage"))
  110. } else {
  111. titleRelay.accept(filterGroups.map { $0 ?? NSLocalizedString("default") }.joined(separator: " , "))
  112. }
  113. }).disposed(by: rx.disposeBag)
  114. // 切换分组和更改搜索词时,更新数据源
  115. Observable
  116. .combineLatest(filterGroups, input.searchText)
  117. .subscribe(onNext: { [weak self] groups, searchText in
  118. self?.results = self?.getResults(filterGroups: groups, searchText: searchText)
  119. }).disposed(by: rx.disposeBag)
  120. // 切换分组和下拉刷新时,重新刷新列表
  121. Observable
  122. .merge(
  123. input.refresh.asObservable().map { () },
  124. filterGroups.map { _ in () },
  125. input.searchText.asObservable().map { _ in () }
  126. )
  127. .subscribe(onNext: { [weak self] in
  128. guard let strongSelf = self else { return }
  129. strongSelf.page = 0
  130. messagesRelay.accept(
  131. messagesToMessageSection(
  132. messages: strongSelf.getNextPage()
  133. )
  134. )
  135. refreshAction.accept(.endRefresh)
  136. }).disposed(by: rx.disposeBag)
  137. // 加载更多
  138. input.loadMore.asObservable()
  139. .subscribe(onNext: { [weak self] in
  140. guard let strongSelf = self else { return }
  141. let messages = strongSelf.getNextPage()
  142. let cellViewModels = messages.map { message -> MessageTableViewCellViewModel in
  143. MessageTableViewCellViewModel(message: message)
  144. }
  145. refreshAction.accept(.endLoadmore)
  146. if var section = messagesRelay.value.first {
  147. section.messages.append(contentsOf: cellViewModels)
  148. messagesRelay.accept([section])
  149. } else {
  150. messagesRelay.accept([MessageSection(header: "model", messages: cellViewModels)])
  151. }
  152. }).disposed(by: rx.disposeBag)
  153. // 删除message
  154. input.itemDelete.drive(onNext: { [weak self] index in
  155. if var section = messagesRelay.value.first {
  156. if let realm = try? Realm() {
  157. try? realm.write {
  158. let message = self?.results?[index]
  159. message?.isDeleted = true
  160. }
  161. }
  162. section.messages.remove(at: index)
  163. messagesRelay.accept([section])
  164. }
  165. }).disposed(by: rx.disposeBag)
  166. // 批量删除
  167. input.delete.drive(onNext: { [weak self] type in
  168. guard let strongSelf = self else { return }
  169. var date = Date()
  170. switch type {
  171. case .allTime:
  172. date = Date(timeIntervalSince1970: 0)
  173. case .todayAndYesterday:
  174. date = Date.yesterday
  175. case .today:
  176. date = Date().noon
  177. case .lastHour:
  178. date = Date.lastHour
  179. }
  180. if let realm = try? Realm() {
  181. guard let messages = strongSelf.getResults(filterGroups: filterGroups.value, searchText: nil)?.filter("createDate >= %@", date) else {
  182. return
  183. }
  184. // 不再使用icecream 了
  185. // 所以需要手动删除
  186. // try? realm.write {
  187. // for msg in messages {
  188. // msg.isDeleted = true
  189. // }
  190. // }
  191. try? realm.write {
  192. for msg in messages {
  193. msg.isDeleted = true
  194. }
  195. realm.delete(messages)
  196. }
  197. }
  198. strongSelf.page = 0
  199. messagesRelay.accept(messagesToMessageSection(messages: strongSelf.getNextPage()))
  200. }).disposed(by: rx.disposeBag)
  201. // 群组筛选
  202. let groupFilter = input.groupTap.compactMap { () -> GroupFilterViewModel? in
  203. if let realm = try? Realm() {
  204. let groups = realm.objects(Message.self)
  205. .filter("isDeleted != true")
  206. .distinct(by: ["group"])
  207. .value(forKeyPath: "group") as? [String?]
  208. let groupModels = groups?.compactMap { groupName -> GroupFilterModel in
  209. var check = true
  210. if filterGroups.value.count > 0 {
  211. check = filterGroups.value.contains(groupName)
  212. }
  213. return GroupFilterModel(name: groupName, checked: check)
  214. }
  215. if let models = groupModels {
  216. let viewModel = GroupFilterViewModel(groups: models)
  217. // 保存选择的 group
  218. viewModel.done.subscribe(onNext: { filterGroups in
  219. Settings["me.fin.filterGroups"] = filterGroups
  220. }).disposed(by: viewModel.rx.disposeBag)
  221. // 将选择绑定到当前页面
  222. viewModel.done
  223. .bind(to: filterGroups)
  224. .disposed(by: viewModel.rx.disposeBag)
  225. return viewModel
  226. }
  227. }
  228. return nil
  229. }
  230. return Output(
  231. messages: messagesRelay.asDriver(onErrorJustReturn: []),
  232. refreshAction: refreshAction.asDriver(),
  233. alertMessage: alertMessage,
  234. groupFilter: groupFilter.asDriver(),
  235. title: titleRelay.asDriver()
  236. )
  237. }
  238. }