// // 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 let selectedDateKey: BehaviorRelay let stayPoints = BehaviorRelay<[StayPoint]>(value: []) let trajectoryPoints = BehaviorRelay<[TrackPoint]>(value: []) let isEmpty = BehaviorRelay(value: true) let loading = BehaviorRelay(value: false) private let disposeBag = DisposeBag() let dateItems: [DateItem] init(members: [GroupMemberModel], selectedUserId: String, initialDate: Date = Date()) { self.members = members self.dateItems = Self.makeDateItems(around: initialDate) self.selectedMemberId = BehaviorRelay(value: selectedUserId) self.selectedDateKey = BehaviorRelay(value: Self.dayKey(initialDate)) bindSelection() reloadPlayback() } private func bindSelection() { selectedMemberId .asObservable() .skip(1) .subscribe(onNext: { [weak self] _ in self?.reloadPlayback() }) .disposed(by: disposeBag) selectedDateKey .asObservable() .skip(1) .subscribe(onNext: { [weak self] _ in self?.reloadPlayback() }) .disposed(by: disposeBag) } private func reloadPlayback() { let userId = selectedMemberId.value let dateKey = selectedDateKey.value loadPlayback(userId: userId, dateKey: dateKey) } private func loadPlayback(userId: String, dateKey: String) { if userId.isEmpty { clearPlayback() return } loading.accept(true) DrivingService.playback(user_id: userId, date: dateKey) .observe(on: MainScheduler.instance) .subscribe(onNext: { [weak self] response in self?.applyPlayback(response) }, onError: { [weak self] _ in self?.clearPlayback() }) .disposed(by: disposeBag) } private func applyPlayback(_ response: ScheduleRecordListResponse) { let trips = response.list let stays = trips.flatMap { $0.stay_points }.sorted { lhs, rhs in lhs.start_time > rhs.start_time } let path = trips.flatMap { $0.trajectory_path } stayPoints.accept(stays) trajectoryPoints.accept(path) isEmpty.accept(stays.isEmpty && path.isEmpty) loading.accept(false) } private func clearPlayback() { stayPoints.accept([]) trajectoryPoints.accept([]) isEmpty.accept(true) loading.accept(false) } private static func dayKey(_ date: Date) -> String { date.toFormat("yyyy-MM-dd") } private static func makeDateItems(around date: Date) -> [DateItem] { let calendar = Calendar.current let today = calendar.startOfDay(for: date) var items: [DateItem] = [] for offset in -4...0 { guard let day = calendar.date(byAdding: .day, value: offset, to: today) else { continue } let title: String switch offset { case 0: title = "今天" case -1: title = "昨天" default: title = day.toFormat("MM-dd") } items.append(DateItem(date: day, title: title, key: dayKey(day))) } return items } }