123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- package com.codeskraps.publicpool.presentation.dashboard
- import cafe.adriel.voyager.core.model.StateScreenModel
- import cafe.adriel.voyager.core.model.screenModelScope
- import com.codeskraps.publicpool.domain.model.ChartDataPoint
- import com.codeskraps.publicpool.domain.usecase.CalculateTwoHourAverageUseCase
- import com.codeskraps.publicpool.domain.usecase.GetChartDataUseCase
- import com.codeskraps.publicpool.domain.usecase.GetClientInfoUseCase
- import com.codeskraps.publicpool.domain.usecase.GetNetworkInfoUseCase
- import com.codeskraps.publicpool.domain.usecase.GetWalletAddressUseCase
- import com.codeskraps.publicpool.di.AppReadinessState
- import kotlinx.coroutines.Job
- import kotlinx.coroutines.channels.Channel
- import kotlinx.coroutines.flow.*
- import kotlinx.coroutines.launch
- class DashboardScreenModel(
- private val getWalletAddressUseCase: GetWalletAddressUseCase,
- private val getNetworkInfoUseCase: GetNetworkInfoUseCase,
- private val getClientInfoUseCase: GetClientInfoUseCase,
- private val getChartDataUseCase: GetChartDataUseCase,
- private val calculateTwoHourAverageUseCase: CalculateTwoHourAverageUseCase,
- private val appReadinessState: AppReadinessState
- ) : StateScreenModel<DashboardState>(DashboardState()) {
- private val _effect = Channel<DashboardEffect>()
- val effect = _effect.receiveAsFlow()
- private var dataLoadingJob: Job? = null
- init {
- // Start loading data immediately
- handleEvent(DashboardEvent.LoadData)
- }
- fun handleEvent(event: DashboardEvent) {
- when (event) {
- DashboardEvent.LoadData -> loadInitialData()
- DashboardEvent.RefreshData -> refreshData()
- DashboardEvent.GoToSettings -> sendEffect(DashboardEffect.NavigateToSettings)
- // Internal Events triggered by data loading flows/calls
- is DashboardEvent.WalletAddressLoaded -> processWalletAddress(event.address)
- is DashboardEvent.NetworkInfoResult -> processNetworkInfoResult(event.result)
- is DashboardEvent.ClientInfoResult -> processClientInfoResult(event.result)
- is DashboardEvent.ChartDataResult -> processChartDataResult(event.result)
- }
- }
- private fun loadInitialData() {
- // Collect wallet address changes
- screenModelScope.launch {
- getWalletAddressUseCase()
- .onStart { mutableState.update { it.copy(isWalletLoading = true) } }
- .catch { e ->
- mutableState.update { it.copy(isWalletLoading = false, errorMessage = "Failed to load wallet address") }
- sendEffect(DashboardEffect.ShowErrorSnackbar("Error loading wallet: ${e.message}"))
- }
- .collect { address ->
- handleEvent(DashboardEvent.WalletAddressLoaded(address))
- }
- }
- // Fetch Network Info (doesn't depend on wallet)
- fetchNetworkInfo()
- }
- private fun processWalletAddress(address: String?) {
- appReadinessState.setReady()
- mutableState.update { it.copy(walletAddress = address, isWalletLoading = false) }
- if (address != null && address.isNotBlank()) {
- // Wallet address available, fetch client-specific data
- fetchClientInfoAndChartData(address)
- } else {
- // No wallet address, clear client data and show appropriate message/state
- mutableState.update {
- it.copy(
- clientInfo = null,
- chartData = emptyList(),
- chartDataTwoHourAvg = emptyList(),
- isClientInfoLoading = false,
- isChartDataLoading = false,
- errorMessage = if (!it.isWalletLoading) "Please set a wallet address in Settings" else it.errorMessage
- )
- }
- // Cancel any ongoing client/chart data fetching if wallet becomes null/blank
- dataLoadingJob?.cancel()
- dataLoadingJob = null
- }
- }
- private fun refreshData() {
- dataLoadingJob?.cancel() // Cancel previous jobs if any
- mutableState.update { it.copy(errorMessage = null) } // Clear previous errors
- fetchNetworkInfo()
- state.value.walletAddress?.let { address ->
- if (address.isNotBlank()) {
- fetchClientInfoAndChartData(address, isRefresh = true)
- }
- }
- }
- private fun fetchNetworkInfo() {
- screenModelScope.launch {
- mutableState.update { it.copy(isNetworkLoading = true) }
- val result = getNetworkInfoUseCase()
- handleEvent(DashboardEvent.NetworkInfoResult(result))
- }
- }
- private fun fetchClientInfoAndChartData(address: String, isRefresh: Boolean = false) {
- dataLoadingJob?.cancel() // Cancel previous loads before starting new ones
- dataLoadingJob = screenModelScope.launch {
- mutableState.update {
- it.copy(
- isClientInfoLoading = true,
- isChartDataLoading = true,
- // Consider clearing previous data on refresh if desired
- // clientInfo = if (isRefresh) it.clientInfo else null,
- // chartData = if (isRefresh) it.chartData else emptyList(),
- // chartDataTwoHourAvg = if (isRefresh) it.chartDataTwoHourAvg else emptyList()
- )
- }
- // Launch both fetches concurrently
- launch {
- val clientInfoResult = getClientInfoUseCase(address)
- handleEvent(DashboardEvent.ClientInfoResult(clientInfoResult))
- }
- launch {
- val chartDataResult = getChartDataUseCase(address)
- handleEvent(DashboardEvent.ChartDataResult(chartDataResult))
- }
- }
- }
- private fun processNetworkInfoResult(result: Result<com.codeskraps.publicpool.domain.model.NetworkInfo>) {
- result.onSuccess {
- mutableState.update { s -> s.copy(networkInfo = it, isNetworkLoading = false) }
- }.onFailure {
- mutableState.update { s -> s.copy(isNetworkLoading = false, errorMessage = "Failed to load network info") }
- sendEffect(DashboardEffect.ShowErrorSnackbar("Network Error: ${it.message}"))
- }
- }
- private fun processClientInfoResult(result: Result<com.codeskraps.publicpool.domain.model.ClientInfo>) {
- result.onSuccess {
- mutableState.update { s -> s.copy(clientInfo = it, isClientInfoLoading = false) }
- }.onFailure {
- mutableState.update { s -> s.copy(isClientInfoLoading = false, errorMessage = "Failed to load client info") }
- sendEffect(DashboardEffect.ShowErrorSnackbar("Client Info Error: ${it.message}"))
- }
- }
- private fun processChartDataResult(result: Result<List<ChartDataPoint>>) {
- result.onSuccess {
- // Calculate 2-hour average from the fetched 10-min data
- val twoHourAvg = calculateTwoHourAverageUseCase(it)
- mutableState.update { s ->
- s.copy(
- chartData = it,
- chartDataTwoHourAvg = twoHourAvg,
- isChartDataLoading = false
- )
- }
- }.onFailure {
- mutableState.update { s -> s.copy(isChartDataLoading = false, errorMessage = "Failed to load chart data") }
- sendEffect(DashboardEffect.ShowErrorSnackbar("Chart Data Error: ${it.message}"))
- }
- }
- private fun sendEffect(effectToSend: DashboardEffect) {
- screenModelScope.launch {
- _effect.send(effectToSend)
- }
- }
- }
|