From 050cf2dbfdc5d84a3dea3a601c4e9ba161134c2f Mon Sep 17 00:00:00 2001 From: lipsa-b Date: Mon, 27 Jul 2026 09:05:21 +0530 Subject: [PATCH 1/6] handling the error --- .../detail/AssignmentDetailScreen.kt | 4 ++- .../detail/AssignmentDetailViewModel.kt | 28 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/assignment/detail/AssignmentDetailScreen.kt b/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/assignment/detail/AssignmentDetailScreen.kt index d9af0e4bf..f71336f7f 100644 --- a/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/assignment/detail/AssignmentDetailScreen.kt +++ b/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/assignment/detail/AssignmentDetailScreen.kt @@ -115,7 +115,9 @@ fun AssignmentDetailScreen( timeZoneId = TimeZone.currentSystemDefault().id, ) - RespectDataLoadHost(uiState.assignmentProgress) { + RespectDataLoadHost(uiState.assignmentProgress, + modifier = Modifier.fillMaxSize().padding(vertical = 10.dp) + ) { Box(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.fillMaxSize()) { if (!uiState.isFullscreen) { diff --git a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt index 4b668a489..fe665a91c 100644 --- a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt +++ b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt @@ -3,11 +3,13 @@ package world.respect.shared.viewmodel.assignment.detail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute +import io.github.aakira.napier.Napier import io.ktor.http.Url import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull @@ -23,10 +25,12 @@ import org.koin.core.scope.Scope import world.respect.datalayer.SchoolDataSource import world.respect.datalayer.db.school.ext.isAdminOrTeacher import world.respect.datalayer.db.school.ext.isStudent +import world.respect.lib.dataloadstate.DataErrorResult import world.respect.lib.dataloadstate.DataLoadParams import world.respect.lib.dataloadstate.DataLoadState import world.respect.lib.dataloadstate.DataLoadingState import world.respect.lib.dataloadstate.DataReadyState +import world.respect.lib.dataloadstate.NoDataLoadedState import world.respect.lib.dataloadstate.ext.dataOrNull import world.respect.lib.opds.model.OpdsPublication import world.respect.lib.xapi.composites.AssignmentAndProgress @@ -234,9 +238,22 @@ class AssignmentDetailViewModel( progress = state.data.progress.sortedBy { it.actor.name?.lowercase() ?: "" } ) ) - } else state + } else { + state + } + }.map { state -> + if (state is NoDataLoadedState && state.reason == NoDataLoadedState.Reason.NOT_FOUND) { + DataErrorResult( + error = IllegalStateException(), + metaInfo = state.metaInfo + ) + } else { + state + } + }.catch { e -> + Napier.w("AssignmentDetailViewModel: assignment progress flow error", e) + emit(DataErrorResult(error = e)) }.shareIn(viewModelScope, SharingStarted.Lazily) - launch { assignmentProgressFlow.collect { assignmentAndProgress -> _appUiState.update { appState -> @@ -299,7 +316,10 @@ class AssignmentDetailViewModel( fun taskInfoFlowFor(url: Url): Flow> { return schoolDataSource.opdsPublicationDataSource.getByUrlAsFlow( url = url, params = DataLoadParams(), null, null - ) + ).catch { e -> + Napier.w("AssignmentDetailViewModel: failed loading task info for $url", e) + emit(DataErrorResult(error = e)) + } } fun onClickTask(activity: XapiActivity) { @@ -340,4 +360,4 @@ class AssignmentDetailViewModel( ) ) } -} +} \ No newline at end of file From ab388fc881e1531c003f4ac92f5e982dde8f893e Mon Sep 17 00:00:00 2001 From: lipsa-b Date: Mon, 27 Jul 2026 20:36:36 +0530 Subject: [PATCH 2/6] handling the error --- .../detail/AssignmentDetailViewModel.kt | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt index fe665a91c..e98f4e2b9 100644 --- a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt +++ b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt @@ -32,6 +32,7 @@ import world.respect.lib.dataloadstate.DataLoadingState import world.respect.lib.dataloadstate.DataReadyState import world.respect.lib.dataloadstate.NoDataLoadedState import world.respect.lib.dataloadstate.ext.dataOrNull +import world.respect.lib.dataloadstate.ext.toPrettyString import world.respect.lib.opds.model.OpdsPublication import world.respect.lib.xapi.composites.AssignmentAndProgress import world.respect.lib.xapi.composites.XapiActorAndAssignmentProgress @@ -242,10 +243,12 @@ class AssignmentDetailViewModel( state } }.map { state -> - if (state is NoDataLoadedState && state.reason == NoDataLoadedState.Reason.NOT_FOUND) { - DataErrorResult( - error = IllegalStateException(), - metaInfo = state.metaInfo + if (state is DataReadyState && state.data.progress.isEmpty()) { + NoDataLoadedState( + reason = NoDataLoadedState.Reason.NOT_FOUND, + metaInfo = state.metaInfo, + localState = state.localState, + remoteState = state.remoteState, ) } else { state @@ -316,7 +319,14 @@ class AssignmentDetailViewModel( fun taskInfoFlowFor(url: Url): Flow> { return schoolDataSource.opdsPublicationDataSource.getByUrlAsFlow( url = url, params = DataLoadParams(), null, null - ).catch { e -> + ).map { state -> + val remoteErr = state.remoteState as? DataErrorResult<*> + if(remoteErr != null) { + DataErrorResult(error = remoteErr.error, metaInfo = state.metaInfo, localState = state.localState, remoteState = state.remoteState) + } else { + state + } + }.catch { e -> Napier.w("AssignmentDetailViewModel: failed loading task info for $url", e) emit(DataErrorResult(error = e)) } From e4e2df3441edaa5be0edbfa5f58e2fd937936142 Mon Sep 17 00:00:00 2001 From: lipsa-b Date: Mon, 27 Jul 2026 21:04:09 +0530 Subject: [PATCH 3/6] Update AssignmentDetailViewModel to emit `DataErrorResult` when the device is offline. --- .../assignment/detail/AssignmentDetailViewModel.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt index e98f4e2b9..196b60a24 100644 --- a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt +++ b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt @@ -3,6 +3,7 @@ package world.respect.shared.viewmodel.assignment.detail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute +import com.ustadmobile.libcache.connectivitymonitor.ConnectivityMonitor import io.github.aakira.napier.Napier import io.ktor.http.Url import kotlinx.coroutines.flow.Flow @@ -11,6 +12,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOf @@ -188,6 +190,7 @@ class AssignmentDetailViewModel( private val schoolDataSource: SchoolDataSource by inject() private val _uiState = MutableStateFlow(AssignmentDetailUiState()) + private val connectivityMonitor: ConnectivityMonitor by inject() val uiState = _uiState.asStateFlow() @@ -253,6 +256,16 @@ class AssignmentDetailViewModel( } else { state } + }.combine(connectivityMonitor.statusFlow) { state, connectivity -> + if (!connectivity.isConnected && state !is DataErrorResult) { + DataErrorResult( + error = kotlinx.io.IOException(), + metaInfo = state.metaInfo, + localState = state, + ) + } else { + state + } }.catch { e -> Napier.w("AssignmentDetailViewModel: assignment progress flow error", e) emit(DataErrorResult(error = e)) From 53d106011a88c4e144ae6bbd41bcc2e7b9c69f3e Mon Sep 17 00:00:00 2001 From: pooja Date: Tue, 28 Jul 2026 13:25:43 +0400 Subject: [PATCH 4/6] Update `.gitignore` to ignore the `.maestro/flows/.maestro/screenshots` directory. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b9c215fe3..ee784eff3 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,4 @@ respect-server/log/ /respect-datalayer-repository/log/respect-server.log /respect-lib-shared/log/respect-server.log /.maestro/video-downloader/cypress/screenshots - +/.maestro/flows/.maestro/screenshots From e7ffde734be7f89674aebb54771d3d7156dac416 Mon Sep 17 00:00:00 2001 From: lipsa-b Date: Tue, 28 Jul 2026 17:18:28 +0530 Subject: [PATCH 5/6] fix --- .../detail/AssignmentDetailViewModel.kt | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt index 196b60a24..49565f6c5 100644 --- a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt +++ b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt @@ -3,7 +3,6 @@ package world.respect.shared.viewmodel.assignment.detail import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute -import com.ustadmobile.libcache.connectivitymonitor.ConnectivityMonitor import io.github.aakira.napier.Napier import io.ktor.http.Url import kotlinx.coroutines.flow.Flow @@ -12,7 +11,6 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOf @@ -190,7 +188,6 @@ class AssignmentDetailViewModel( private val schoolDataSource: SchoolDataSource by inject() private val _uiState = MutableStateFlow(AssignmentDetailUiState()) - private val connectivityMonitor: ConnectivityMonitor by inject() val uiState = _uiState.asStateFlow() @@ -245,29 +242,8 @@ class AssignmentDetailViewModel( } else { state } - }.map { state -> - if (state is DataReadyState && state.data.progress.isEmpty()) { - NoDataLoadedState( - reason = NoDataLoadedState.Reason.NOT_FOUND, - metaInfo = state.metaInfo, - localState = state.localState, - remoteState = state.remoteState, - ) - } else { - state - } - }.combine(connectivityMonitor.statusFlow) { state, connectivity -> - if (!connectivity.isConnected && state !is DataErrorResult) { - DataErrorResult( - error = kotlinx.io.IOException(), - metaInfo = state.metaInfo, - localState = state, - ) - } else { - state - } }.catch { e -> - Napier.w("AssignmentDetailViewModel: assignment progress flow error", e) + Napier.w("Assignment progress flow error", e) emit(DataErrorResult(error = e)) }.shareIn(viewModelScope, SharingStarted.Lazily) launch { @@ -340,7 +316,7 @@ class AssignmentDetailViewModel( state } }.catch { e -> - Napier.w("AssignmentDetailViewModel: failed loading task info for $url", e) + Napier.w("failed loading task info for $url", e) emit(DataErrorResult(error = e)) } } From 55552eccef1841eb7761e78ffdb831827ec4584b Mon Sep 17 00:00:00 2001 From: lipsa-b Date: Tue, 28 Jul 2026 17:18:58 +0530 Subject: [PATCH 6/6] fix --- .../viewmodel/assignment/detail/AssignmentDetailViewModel.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt index 49565f6c5..6ab012dc3 100644 --- a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt +++ b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/assignment/detail/AssignmentDetailViewModel.kt @@ -30,9 +30,7 @@ import world.respect.lib.dataloadstate.DataLoadParams import world.respect.lib.dataloadstate.DataLoadState import world.respect.lib.dataloadstate.DataLoadingState import world.respect.lib.dataloadstate.DataReadyState -import world.respect.lib.dataloadstate.NoDataLoadedState import world.respect.lib.dataloadstate.ext.dataOrNull -import world.respect.lib.dataloadstate.ext.toPrettyString import world.respect.lib.opds.model.OpdsPublication import world.respect.lib.xapi.composites.AssignmentAndProgress import world.respect.lib.xapi.composites.XapiActorAndAssignmentProgress