Skip to content

Commit

Permalink
fix: Restore ability to cancel an in-progress upload
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisCAD committed Mar 6, 2025
1 parent 8aa3764 commit 8abf720
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,27 @@ class UploadViewModel @Inject constructor() : ViewModel() {
val abandonUploadRequest = CallableState<Unit>()

init {
viewModelScope.launch { handleCancellationRequests() }
viewModelScope.launch {
UploadForegroundService.uploadStateFlow.collectLatest { uploadState ->
if (uploadState is UploadState.Retry) handleRetryRequests()
}
}
}

private suspend fun handleCancellationRequests(): Nothing = repeatWhileActive {
abandonUploadRequest.awaitOneCall()
UploadForegroundService.cancelUpload()
}

private suspend fun handleRetryRequests(): Nothing = repeatWhileActive {
val shouldRetry = raceOf(
{ retryRequest.awaitOneCall(); true },
{ editRequest.awaitOneCall(); false },
{ abandonUploadRequest.awaitOneCall(); null },
)
when (shouldRetry) {
true -> UploadForegroundService.retry()
false -> UploadForegroundService.giveUp()
null -> {
UploadForegroundService.removeAllFiles()
UploadForegroundService.giveUp()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.*
import splitties.coroutines.raceOf
import splitties.coroutines.repeatWhileActive
import splitties.experimental.ExperimentalSplittiesApi
import splitties.init.appCtx
Expand All @@ -60,6 +61,7 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi

private val startSignal = Channel<StartUploadRequest>()

private val cancelTransferSignals = Channel<Unit>()
private val shouldRetrySignals = Channel<Boolean>()

private val pickedFilesExtractor: PickedFilesExtractor = PickedFilesExtractorImpl().also {
Expand Down Expand Up @@ -123,6 +125,10 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
shouldRetrySignals.send(false)
}

suspend fun cancelUpload() {
cancelTransferSignals.send(Unit)
}

private fun keepServiceRunningWhileNeeded() {
val needsToKeepFileUris = isHandlingPickedFilesFlow.transformLatest { isHandlingFiles ->
if (isHandlingFiles) emit(true)
Expand Down Expand Up @@ -202,21 +208,24 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
)

//TODO[UL-retry]: Once we support resuming the upload:
// 1. Remove this big `isInternetConnectedFlow` thing and move it to just the uploader part.
// 1. Remove `isInternetConnectedFlow.mapLatest` from `tryCompletingWithInternetUnlessCancelled`,
// and move it to just the uploader part.
// 2. Remove the loop (repeatWhileActive) below.
// 3. In the uploader, use a function named `uploadAllRemainingWithRetries` or something.
val transferUuid = isInternetConnectedFlow.mapLatest { isInternetConnected ->
if (isInternetConnected.not()) {
val transferUuid = tryCompletingWithInternetUnlessCancelled(
valueIfCancelled = null,
withoutInternet = {
currentState = uploadStateFlow.filterIsInstance<UploadState.Ongoing>().first().copy(
status = Status.WaitingForInternet
)
awaitCancellation()
}
) {
repeatWhileActive retryLoop@{
val result = startUploadSession(
startRequest = startRequest,
updateState = { currentState = it }
) ?: return@mapLatest null
) ?: return@tryCompletingWithInternetUnlessCancelled null
val uploader = TransferUploader(
uploadManager = uploadManager,
fileChunkSizeManager = fileChunkSizeManager,
Expand All @@ -242,11 +251,11 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
if (shouldRetry()) {
return@retryLoop
} else {
return@mapLatest null
return@tryCompletingWithInternetUnlessCancelled null
}
}.onSuccess { uuid -> return@mapLatest uuid }
}.onSuccess { uuid -> return@tryCompletingWithInternetUnlessCancelled uuid }
}
}.first()
}
currentState = if (transferUuid != null) {
val url = sharedApiUrlCreator.shareTransferUrl(transferUuid)
val transferType = startRequest.info.type
Expand All @@ -268,6 +277,19 @@ class UploadForegroundService : ForegroundService(Companion, redeliverIntentIfKi
}
}

private suspend fun <R> tryCompletingWithInternetUnlessCancelled(
valueIfCancelled: R,
withoutInternet: suspend () -> R,
block: suspend () -> R
): R? = raceOf({
isInternetConnectedFlow.mapLatest { isInternetConnected ->
if (isInternetConnected) block() else withoutInternet()
}.first()
}, {
cancelTransferSignals.receive()
valueIfCancelled
})

private suspend fun shouldRetry(): Boolean = shouldRetrySignals.receive()

private suspend fun startUploadSession(
Expand Down

0 comments on commit 8abf720

Please sign in to comment.