Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions course/src/main/assets/js_injection/completions.js
Original file line number Diff line number Diff line change
@@ -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();
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -95,6 +96,9 @@ class CourseContainerViewModel(
val isNavigationEnabled: StateFlow<Boolean> =
_isNavigationEnabled.asStateFlow()

private var updateDataJob: Job? = null
private var pendingStructureRefresh = false

private var _courseDetails: CourseEnrollmentDetails? = null
val courseDetails: CourseEnrollmentDetails?
get() = _courseDetails
Expand Down Expand Up @@ -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)
}
}

Expand Down
Loading