Skip to content

Commit f774d14

Browse files
committed
fix(details): improve coroutine management and cleanup profile UI
This commit addresses potential memory leaks and state inconsistencies in `DetailsViewModel` by refining job management and simplifies the `ProfileRoot` UI by removing the redundant back navigation button. - **fix(details)**: Updated `InstallWithExternalApp` logic to correctly handle `CancellationException`, resetting the UI state and clearing asset references when a job is cancelled. - **fix(details)**: Improved `currentDownloadJob` lifecycle management using `invokeOnCompletion` to prevent race conditions when multiple jobs are launched. - **refactor(profile)**: Removed the manual navigation icon from `TopAppBar` in `ProfileRoot.kt` and cleaned up associated imports.
1 parent 71d707e commit f774d14

File tree

2 files changed

+15
-25
lines changed
  • feature
    • details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation
    • profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation

2 files changed

+15
-25
lines changed

feature/details/presentation/src/commonMain/kotlin/zed/rainxch/details/presentation/DetailsViewModel.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import zed.rainxch.details.presentation.model.LogResult
4848
import zed.rainxch.details.presentation.model.SupportedLanguages
4949
import zed.rainxch.details.presentation.model.TranslationState
5050
import java.util.concurrent.atomic.AtomicBoolean
51+
import kotlin.coroutines.cancellation.CancellationException
5152
import kotlin.time.Clock.System
5253
import kotlin.time.ExperimentalTime
5354

@@ -779,7 +780,7 @@ class DetailsViewModel(
779780

780781
DetailsAction.InstallWithExternalApp -> {
781782
currentDownloadJob?.cancel()
782-
currentDownloadJob = viewModelScope.launch {
783+
val job = viewModelScope.launch {
783784
try {
784785
val primary = _state.value.primaryAsset
785786
val release = _state.value.selectedRelease
@@ -832,6 +833,11 @@ class DetailsViewModel(
832833
result = LogResult.OpenedInExternalInstaller
833834
)
834835
}
836+
} catch (e: CancellationException) {
837+
logger.debug("Install with external app cancelled")
838+
_state.value = _state.value.copy(downloadStage = DownloadStage.IDLE)
839+
currentAssetName = null
840+
throw e
835841
} catch (t: Throwable) {
836842
logger.error("Failed to install with external app: ${t.message}")
837843
_state.value = _state.value.copy(
@@ -850,7 +856,12 @@ class DetailsViewModel(
850856
)
851857
}
852858
}
853-
} finally {
859+
}
860+
}
861+
862+
currentDownloadJob = job
863+
job.invokeOnCompletion {
864+
if (currentDownloadJob === job) {
854865
currentDownloadJob = null
855866
}
856867
}

feature/profile/presentation/src/commonMain/kotlin/zed/rainxch/profile/presentation/ProfileRoot.kt

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,11 @@ package zed.rainxch.profile.presentation
22

33
import androidx.compose.foundation.layout.Spacer
44
import androidx.compose.foundation.layout.fillMaxSize
5-
import androidx.compose.foundation.layout.fillMaxWidth
65
import androidx.compose.foundation.layout.height
76
import androidx.compose.foundation.layout.padding
8-
import androidx.compose.foundation.layout.size
97
import androidx.compose.foundation.lazy.LazyColumn
10-
import androidx.compose.material.icons.Icons
11-
import androidx.compose.material.icons.automirrored.filled.ArrowBack
128
import androidx.compose.material3.ExperimentalMaterial3Api
139
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
14-
import androidx.compose.material3.Icon
15-
import androidx.compose.material3.IconButton
16-
import androidx.compose.material3.IconButtonDefaults
1710
import androidx.compose.material3.MaterialTheme
1811
import androidx.compose.material3.Scaffold
1912
import androidx.compose.material3.SnackbarHost
@@ -150,7 +143,7 @@ fun ProfileScreen(
150143
)
151144
},
152145
topBar = {
153-
TopAppBar(onAction)
146+
TopAppBar()
154147
},
155148
containerColor = MaterialTheme.colorScheme.background,
156149
modifier = Modifier.liquefiable(liquidState)
@@ -212,22 +205,8 @@ fun ProfileScreen(
212205

213206
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
214207
@Composable
215-
private fun TopAppBar(onAction: (ProfileAction) -> Unit) {
208+
private fun TopAppBar() {
216209
TopAppBar(
217-
navigationIcon = {
218-
IconButton(
219-
shapes = IconButtonDefaults.shapes(),
220-
onClick = {
221-
onAction(ProfileAction.OnNavigateBackClick)
222-
}
223-
) {
224-
Icon(
225-
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
226-
contentDescription = stringResource(Res.string.navigate_back),
227-
modifier = Modifier.size(24.dp)
228-
)
229-
}
230-
},
231210
title = {
232211
Text(
233212
text = stringResource(Res.string.profile_title),

0 commit comments

Comments
 (0)