From b6ea0afa340448eafc5be8360c661115951c3a21 Mon Sep 17 00:00:00 2001 From: Tenzin Choeying Date: Tue, 4 Aug 2026 15:06:51 +0530 Subject: [PATCH] fix: reflect back the changes after completing MCQs without the need to refresh the page or getting out of course and back in --- .../main/assets/js_injection/completions.js | 10 ++++-- .../domain/interactor/CourseInteractor.kt | 6 +++- .../container/CourseContainerViewModel.kt | 34 +++++++++++++------ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/course/src/main/assets/js_injection/completions.js b/course/src/main/assets/js_injection/completions.js index 329de07d8..d4172c04d 100644 --- a/course/src/main/assets/js_injection/completions.js +++ b/course/src/main/assets/js_injection/completions.js @@ -1,8 +1,12 @@ //Injection to intercept completion state for xBlocks $(document).on("ajaxSuccess", function(event, request, settings) { - console.log("loaded url is = " + settings.url); - if (settings.url.includes("publish_completion") && - request.responseText.includes("ok")) { + var url = settings.url || ""; + // Problems never publish completion; submitting an answer through + // problem_check is what marks them complete on the LMS. + var completionPublished = url.includes("publish_completion") && + request.responseText.includes("ok"); + var problemSubmitted = url.includes("problem_check"); + if (completionPublished || problemSubmitted) { javascript:window.callback.completionSet(); } }); diff --git a/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt b/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt index 543695689..b6f8d6a33 100644 --- a/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt +++ b/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt @@ -2,6 +2,7 @@ package org.openedx.course.domain.interactor import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.last import org.openedx.core.BlockType import org.openedx.core.domain.interactor.CourseInteractor import org.openedx.core.domain.model.Block @@ -33,7 +34,10 @@ class CourseInteractor( courseId: String, isNeedRefresh: Boolean ): CourseStructure { - return repository.getCourseStructureFlow(courseId, isNeedRefresh).first() + // The flow emits cached data before the network result, so first() would + // cancel it before the refresh runs. When refreshing, take the final emission. + val flow = repository.getCourseStructureFlow(courseId, isNeedRefresh) + return if (isNeedRefresh) flow.last() else flow.first() } override suspend fun getCourseStructureFromCache(courseId: String): CourseStructure { diff --git a/course/src/main/java/org/openedx/course/presentation/container/CourseContainerViewModel.kt b/course/src/main/java/org/openedx/course/presentation/container/CourseContainerViewModel.kt index b0cbb3390..f74c9c973 100644 --- a/course/src/main/java/org/openedx/course/presentation/container/CourseContainerViewModel.kt +++ b/course/src/main/java/org/openedx/course/presentation/container/CourseContainerViewModel.kt @@ -7,6 +7,7 @@ import androidx.core.graphics.createBitmap import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -95,6 +96,9 @@ class CourseContainerViewModel( val isNavigationEnabled: StateFlow = _isNavigationEnabled.asStateFlow() + private var updateDataJob: Job? = null + private var pendingStructureRefresh = false + private var _courseDetails: CourseEnrollmentDetails? = null val courseDetails: CourseEnrollmentDetails? get() = _courseDetails @@ -316,16 +320,26 @@ class CourseContainerViewModel( } fun updateData() { - viewModelScope.launch { - try { - interactor.getCourseStructure(courseId, isNeedRefresh = true) - } catch (e: Exception) { - _errorMessage.value = resolveErrorMessage( - throwable = e, - ) - } - _refreshing.value = false - courseNotifier.send(CourseStructureUpdated(courseId)) + // A refresh already running was started before this request, so its response can + // miss the completion that triggered it. Queue a trailing refresh instead of + // launching a parallel one, which would coalesce onto that same stale fetch. + if (updateDataJob?.isActive == true) { + pendingStructureRefresh = true + return + } + updateDataJob = viewModelScope.launch { + do { + pendingStructureRefresh = false + try { + interactor.getCourseStructure(courseId, isNeedRefresh = true) + } catch (e: Exception) { + _errorMessage.value = resolveErrorMessage( + throwable = e, + ) + } + _refreshing.value = false + courseNotifier.send(CourseStructureUpdated(courseId)) + } while (pendingStructureRefresh) } }