162 lines
5.3 KiB
Swift
162 lines
5.3 KiB
Swift
//
|
|
// TodayTrackDetailViewModel.swift
|
|
// QuickLocation
|
|
//
|
|
|
|
import Foundation
|
|
import RxSwift
|
|
import RxCocoa
|
|
import SwiftDate
|
|
|
|
final class TodayTrackDetailViewModel {
|
|
|
|
struct DateItem {
|
|
let date: Date
|
|
let title: String
|
|
let key: String
|
|
}
|
|
|
|
let members: [GroupMemberModel]
|
|
let selectedMemberId: BehaviorRelay<String>
|
|
let selectedDateKey: BehaviorRelay<String>
|
|
let trips = BehaviorRelay<[ScheduleRecordModel]>(value: [])
|
|
let selectedTripId = BehaviorRelay<String?>(value: nil)
|
|
let displayedTrips = BehaviorRelay<[ScheduleRecordModel]>(value: [])
|
|
let isEmpty = BehaviorRelay<Bool>(value: true)
|
|
let loading = BehaviorRelay<Bool>(value: false)
|
|
|
|
private let disposeBag = DisposeBag()
|
|
let dateItems: [DateItem]
|
|
private var requestToken = UUID()
|
|
|
|
init(members: [GroupMemberModel], selectedUserId: String, initialDate: Date = Date()) {
|
|
self.members = members
|
|
self.dateItems = Self.makeDateItems(around: initialDate)
|
|
self.selectedMemberId = BehaviorRelay(value: selectedUserId)
|
|
let defaultKey = Self.dayKey(Self.yesterday(of: initialDate))
|
|
self.selectedDateKey = BehaviorRelay(value: defaultKey)
|
|
bindSelection()
|
|
}
|
|
|
|
func start() {
|
|
reloadTrips()
|
|
}
|
|
|
|
var selectedMember: GroupMemberModel? {
|
|
members.first(where: { $0.user_id == selectedMemberId.value })
|
|
}
|
|
|
|
func selectTrip(_ trip: ScheduleRecordModel) {
|
|
selectedTripId.accept(trip.id)
|
|
applyDisplayedTrips()
|
|
}
|
|
|
|
func dateMillis(for key: String) -> Int64 {
|
|
guard let item = dateItems.first(where: { $0.key == key }) else {
|
|
return Self.dayStartMillis(Self.yesterday(of: Date()))
|
|
}
|
|
return Self.dayStartMillis(item.date)
|
|
}
|
|
|
|
private func bindSelection() {
|
|
Observable.merge(
|
|
selectedMemberId.asObservable().skip(1).map { _ in () },
|
|
selectedDateKey.asObservable().skip(1).map { _ in () }
|
|
)
|
|
.subscribe(onNext: { [weak self] in
|
|
self?.reloadTrips()
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
|
|
private func reloadTrips() {
|
|
let userId = selectedMemberId.value
|
|
let dateKey = selectedDateKey.value
|
|
selectedTripId.accept(nil)
|
|
trips.accept([])
|
|
displayedTrips.accept([])
|
|
isEmpty.accept(false)
|
|
loadTrips(userId: userId, dateKey: dateKey)
|
|
}
|
|
|
|
private func loadTrips(userId: String, dateKey: String) {
|
|
if userId.isEmpty {
|
|
applyTrips([])
|
|
return
|
|
}
|
|
let token = UUID()
|
|
requestToken = token
|
|
loading.accept(true)
|
|
let date = dateMillis(for: dateKey)
|
|
UserService.phoneUsageTrips(userId: userId, date: date)
|
|
.observe(on: MainScheduler.instance)
|
|
.subscribe(onNext: { [weak self] response in
|
|
guard let self, self.requestToken == token else { return }
|
|
self.applyTrips(response.trips)
|
|
}, onError: { [weak self] _ in
|
|
guard let self, self.requestToken == token else { return }
|
|
self.applyTrips([])
|
|
})
|
|
.disposed(by: disposeBag)
|
|
}
|
|
|
|
private func applyTrips(_ list: [ScheduleRecordModel]) {
|
|
let sorted = list.sorted { $0.start_time < $1.start_time }
|
|
trips.accept(sorted)
|
|
if selectedTripId.value == nil || !sorted.contains(where: { $0.id == selectedTripId.value }) {
|
|
selectedTripId.accept(sorted.first?.id)
|
|
}
|
|
applyDisplayedTrips()
|
|
loading.accept(false)
|
|
}
|
|
|
|
private func applyDisplayedTrips() {
|
|
let all = trips.value
|
|
if let id = selectedTripId.value, let one = all.first(where: { $0.id == id }) {
|
|
displayedTrips.accept([one])
|
|
} else {
|
|
displayedTrips.accept([])
|
|
}
|
|
isEmpty.accept(all.isEmpty)
|
|
}
|
|
|
|
private static func yesterday(of date: Date) -> Date {
|
|
var calendar = shanghaiCalendar()
|
|
let today = calendar.startOfDay(for: date)
|
|
return calendar.date(byAdding: .day, value: -1, to: today) ?? today
|
|
}
|
|
|
|
private static func dayKey(_ date: Date) -> String {
|
|
date.in(region: shanghaiRegion()).toFormat("yyyy-MM-dd")
|
|
}
|
|
|
|
static func dayStartMillis(_ date: Date) -> Int64 {
|
|
let start = shanghaiCalendar().startOfDay(for: date)
|
|
return Int64((start.timeIntervalSince1970 * 1000).rounded())
|
|
}
|
|
|
|
private static func makeDateItems(around date: Date) -> [DateItem] {
|
|
var calendar = shanghaiCalendar()
|
|
let today = calendar.startOfDay(for: date)
|
|
var items: [DateItem] = []
|
|
for offset in (-30 ... -1) {
|
|
guard let day = calendar.date(byAdding: .day, value: offset, to: today) else {
|
|
continue
|
|
}
|
|
let title = offset == -1 ? "昨天" : day.in(region: shanghaiRegion()).toFormat("MM-dd")
|
|
items.append(DateItem(date: day, title: title, key: dayKey(day)))
|
|
}
|
|
return items
|
|
}
|
|
|
|
private static func shanghaiCalendar() -> Calendar {
|
|
var calendar = Calendar(identifier: .gregorian)
|
|
calendar.timeZone = TimeZone(identifier: "Asia/Shanghai") ?? .current
|
|
return calendar
|
|
}
|
|
|
|
private static func shanghaiRegion() -> Region {
|
|
Region(zone: TimeZone(identifier: "Asia/Shanghai") ?? .current, locale: Locale(identifier: "zh_CN"))
|
|
}
|
|
}
|