// // MessageListViewModel.swift // Bark // // Created by huangfeng on 2020/11/21. // Copyright © 2020 Fin. All rights reserved. // import Foundation import RealmSwift import RxCocoa import RxDataSources import RxSwift class MessageListViewModel: ViewModel, ViewModelType { struct Input { var refresh: Driver var loadMore: Driver var itemDelete: Driver var itemSelected: Driver var delete: Driver var groupTap: Driver var searchText: Observable } struct Output { var messages: Driver<[MessageSection]> var refreshAction: Driver var alertMessage: Driver<(String, Int)> var groupFilter: Driver var title: Driver } private var results: Results? // 根据群组获取消息 private func getResults(filterGroups: [String?], searchText: String?) -> Results? { if let realm = try? Realm() { var results = realm.objects(Message.self) .filter("isDeleted != true") .sorted(byKeyPath: "createDate", ascending: false) if filterGroups.count > 0 { results = results.filter("group in %@", filterGroups) } if let text = searchText, text.count > 0 { results = results.filter("title contains[c] '\(text)' or body contains[c] '\(text)'") } return results } return nil } private var page = 0 private let pageCount = 20 private func getNextPage() -> [Message] { if let result = results { let startIndex = page * pageCount let endIndex = min(startIndex + pageCount, result.count) guard endIndex > startIndex else { return [] } var messages: [Message] = [] for i in startIndex.. Output { let alertMessage = input.itemSelected.map { [weak self] index in guard let results = self?.results else { return ("", 0) } let message = results[index] // let message = model.message var copyContent: String = "" if let title = message.title { copyContent += "\(title)\n" } if let body = message.body { copyContent += "\(body)\n" } if let url = message.url { copyContent += "\(url)\n" } copyContent = String(copyContent.prefix(copyContent.count - 1)) return (copyContent, index) } // 标题 let titleRelay = BehaviorRelay(value: NSLocalizedString("historyMessage")) // 数据源 let messagesRelay = BehaviorRelay<[MessageSection]>(value: []) // 刷新操作 let refreshAction = BehaviorRelay(value: .none) // 切换群组 let filterGroups: BehaviorRelay<[String?]> = { if let groups: [String?] = Settings["me.fin.filterGroups"] { return BehaviorRelay<[String?]>(value: groups) } return BehaviorRelay<[String?]>(value: []) }() // Message 转 MessageSection func messagesToMessageSection(messages: [Message]) -> [MessageSection] { let cellViewModels = messages.map { message -> MessageTableViewCellViewModel in MessageTableViewCellViewModel(message: message) } return [MessageSection(header: "model", messages: cellViewModels)] } // 切换分组时,更新分组名 filterGroups .subscribe(onNext: { filterGroups in if filterGroups.count <= 0 { titleRelay.accept(NSLocalizedString("historyMessage")) } else { titleRelay.accept(filterGroups.map { $0 ?? NSLocalizedString("default") }.joined(separator: " , ")) } }).disposed(by: rx.disposeBag) // 切换分组和更改搜索词时,更新数据源 Observable .combineLatest(filterGroups, input.searchText) .subscribe(onNext: { [weak self] groups, searchText in self?.results = self?.getResults(filterGroups: groups, searchText: searchText) }).disposed(by: rx.disposeBag) // 切换分组和下拉刷新时,重新刷新列表 Observable .merge( input.refresh.asObservable().map { () }, filterGroups.map { _ in () }, input.searchText.asObservable().map { _ in () } ) .subscribe(onNext: { [weak self] in guard let strongSelf = self else { return } strongSelf.page = 0 messagesRelay.accept( messagesToMessageSection( messages: strongSelf.getNextPage() ) ) refreshAction.accept(.endRefresh) }).disposed(by: rx.disposeBag) // 加载更多 input.loadMore.asObservable() .subscribe(onNext: { [weak self] in guard let strongSelf = self else { return } let messages = strongSelf.getNextPage() let cellViewModels = messages.map { message -> MessageTableViewCellViewModel in MessageTableViewCellViewModel(message: message) } refreshAction.accept(.endLoadmore) if var section = messagesRelay.value.first { section.messages.append(contentsOf: cellViewModels) messagesRelay.accept([section]) } else { messagesRelay.accept([MessageSection(header: "model", messages: cellViewModels)]) } }).disposed(by: rx.disposeBag) // 删除message input.itemDelete.drive(onNext: { [weak self] index in if var section = messagesRelay.value.first { if let realm = try? Realm() { try? realm.write { let message = self?.results?[index] message?.isDeleted = true } } section.messages.remove(at: index) messagesRelay.accept([section]) } }).disposed(by: rx.disposeBag) // 批量删除 input.delete.drive(onNext: { [weak self] type in guard let strongSelf = self else { return } var date = Date() switch type { case .allTime: date = Date(timeIntervalSince1970: 0) case .todayAndYesterday: date = Date.yesterday case .today: date = Date().noon case .lastHour: date = Date.lastHour } if let realm = try? Realm() { guard let messages = strongSelf.getResults(filterGroups: filterGroups.value, searchText: nil)?.filter("createDate >= %@", date) else { return } // 不再使用icecream 了 // 所以需要手动删除 // try? realm.write { // for msg in messages { // msg.isDeleted = true // } // } try? realm.write { for msg in messages { msg.isDeleted = true } realm.delete(messages) } } strongSelf.page = 0 messagesRelay.accept(messagesToMessageSection(messages: strongSelf.getNextPage())) }).disposed(by: rx.disposeBag) // 群组筛选 let groupFilter = input.groupTap.compactMap { () -> GroupFilterViewModel? in if let realm = try? Realm() { let groups = realm.objects(Message.self) .filter("isDeleted != true") .distinct(by: ["group"]) .value(forKeyPath: "group") as? [String?] let groupModels = groups?.compactMap { groupName -> GroupFilterModel in var check = true if filterGroups.value.count > 0 { check = filterGroups.value.contains(groupName) } return GroupFilterModel(name: groupName, checked: check) } if let models = groupModels { let viewModel = GroupFilterViewModel(groups: models) // 保存选择的 group viewModel.done.subscribe(onNext: { filterGroups in Settings["me.fin.filterGroups"] = filterGroups }).disposed(by: viewModel.rx.disposeBag) // 将选择绑定到当前页面 viewModel.done .bind(to: filterGroups) .disposed(by: viewModel.rx.disposeBag) return viewModel } } return nil } return Output( messages: messagesRelay.asDriver(onErrorJustReturn: []), refreshAction: refreshAction.asDriver(), alertMessage: alertMessage, groupFilter: groupFilter.asDriver(), title: titleRelay.asDriver() ) } }