/** * TrackerMap * Copyright (C) 2021-2022 Iván Ávalos , Henoch Ojeda * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package mx.trackermap.TrackerMap.controllers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import mx.trackermap.TrackerMap.client.apis.DevicesApi import mx.trackermap.TrackerMap.client.apis.PositionsApi import mx.trackermap.TrackerMap.client.models.Position import mx.trackermap.TrackerMap.client.models.UnitInformation import mx.trackermap.TrackerMap.utils.Coroutines import kotlin.time.Duration.Companion.seconds import kotlin.time.ExperimentalTime import kotlinx.coroutines.Job @DelicateCoroutinesApi @ExperimentalTime class UnitsController( private val devicesApi: DevicesApi, private val positionsApi: PositionsApi ) { private companion object { const val UPDATE_TIME = 20 } val displayedUnitsFlow = MutableStateFlow>(emptyList()) private val unitsFlow = MutableStateFlow>(emptyList()) private val queryFlow = MutableStateFlow("") private var unitsUpdateJob: Job? = null init { GlobalScope.launch { fetchUnits(this) } GlobalScope.launch { setupFlows() } } private suspend fun fetchUnits(scope: CoroutineScope) { unitsUpdateJob = Coroutines.tickerFlow(UPDATE_TIME.seconds) .map { print("Fetching new positions...\n") positionsApi.positionsGet() } .distinctUntilChanged { old, new -> old.contentEquals(new) } .onEach { positions -> print("Position(s) changed! Reloading data...\n") val devices = devicesApi.devicesGet() val positionsMap: MutableMap = mutableMapOf() positions.forEach { position -> position.deviceId?.let { positionsMap[it] = position } } val units = devices.map { UnitInformation(it, positionsMap[it.id]) } unitsFlow.value = units } .catch { print("Could not retrieve positions\n") } .launchIn(scope) } fun stopUpdates() { unitsUpdateJob?.cancel() } fun search(query: String) { queryFlow.value = query } suspend fun getUnit(deviceId: Int): UnitInformation { val device = devicesApi.devicesGet(id = deviceId).first() val position = positionsApi.positionsGet(id = device.positionId).first() return UnitInformation(device, position) } private suspend fun setupFlows() { unitsFlow.combine(queryFlow) { units, query -> units.filter { unit -> unit.device.name.contains(query, true) || unit.device.contact?.contains(query, true) == true || unit.position?.address?.contains(query, true) == true } }.collect { units -> this.displayedUnitsFlow.value = units } } }