Skip to content

Commit 7c21ef0

Browse files
committed
fix: keep background operations alive across page changes
1 parent f91d4f4 commit 7c21ef0

6 files changed

Lines changed: 116 additions & 70 deletions

File tree

app/src/main/kotlin/features/proxy/server/list/ProxyServerListPage.kt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,12 @@ import features.proxy.server.usecase.ProxyServiceResult
3838
import features.proxy.server.usecase.restartProxyServiceAfterSelection
3939
import features.proxy.server.usecase.runProxyServerLatencyTest
4040
import features.subscription.DefaultSubscriptionGroupId
41+
import kotlinx.coroutines.Dispatchers
4142
import kotlinx.coroutines.flow.distinctUntilChanged
4243
import kotlinx.coroutines.flow.map
4344
import kotlinx.coroutines.launch
4445
import kotlinx.coroutines.sync.Mutex
46+
import kotlinx.coroutines.withContext
4547
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
4648
import top.yukonga.miuix.kmp.basic.Scaffold
4749
import ui.layout.pageContentPaddingWithCutout
@@ -90,12 +92,14 @@ fun ProxyServerListPage(
9092

9193
fun runProxyServiceOperation(operation: suspend () -> Unit) {
9294
if (serviceOperationInProgress) return
93-
scope.launch {
94-
serviceOperationInProgress = true
95+
serviceOperationInProgress = true
96+
services.appScope.launch {
9597
try {
9698
operation()
9799
} finally {
98-
serviceOperationInProgress = false
100+
withContext(Dispatchers.Main.immediate) {
101+
serviceOperationInProgress = false
102+
}
99103
}
100104
}
101105
}
@@ -136,7 +140,7 @@ fun ProxyServerListPage(
136140
mode = mode,
137141
doneTemplate = doneTemplate,
138142
showSingleResult = showSingleResult,
139-
scope = scope,
143+
scope = services.appScope,
140144
stateStore = stateStore,
141145
updateAppState = updateAppState,
142146
proxyLatencyTester = proxyLatencyTester,
@@ -153,7 +157,6 @@ fun ProxyServerListPage(
153157
is ProxyServiceResult.Success -> {
154158
val remarks = server.server.getInfo().remarks
155159
var deleted = false
156-
var nextSelectedServerId = stateStore.state.value.selectedProxyServerId
157160
updateAppState { state ->
158161
val nextServers = state.proxyServers.filterNot { it.id == server.id }
159162
if (nextServers.size == state.proxyServers.size) {
@@ -168,7 +171,6 @@ fun ProxyServerListPage(
168171
} else {
169172
state.selectedProxyServerId
170173
}
171-
nextSelectedServerId = selectedProxyServerId
172174
state.copy(
173175
proxyServers = nextServers,
174176
selectedProxyServerId = selectedProxyServerId,
@@ -177,7 +179,6 @@ fun ProxyServerListPage(
177179
)
178180
}
179181
}
180-
selectedServerId = nextSelectedServerId
181182
if (deleted) {
182183
tipNotifier.show(messages.deletedTemplate.formatTemplate("name" to remarks))
183184
}
@@ -269,6 +270,7 @@ fun ProxyServerListPage(
269270
clipboard = clipboard,
270271
tipNotifier = tipNotifier,
271272
scope = scope,
273+
backgroundScope = services.appScope,
272274
messages = messages,
273275
resultKey = ProxyServerEditResultKey,
274276
serviceOperationInProgress = serviceOperationInProgress,

app/src/main/kotlin/features/proxy/server/list/ProxyServerListTopBar.kt

Lines changed: 61 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ internal fun ProxyServerListTopBar(
6666
clipboard: Clipboard,
6767
tipNotifier: AndroidToastTipNotifier,
6868
scope: CoroutineScope,
69+
backgroundScope: CoroutineScope,
6970
messages: ProxyServerListMessages,
7071
resultKey: String,
7172
serviceOperationInProgress: Boolean,
@@ -92,6 +93,7 @@ internal fun ProxyServerListTopBar(
9293
clipboard = clipboard,
9394
tipNotifier = tipNotifier,
9495
scope = scope,
96+
backgroundScope = backgroundScope,
9597
messages = messages,
9698
resultKey = resultKey,
9799
)
@@ -109,6 +111,7 @@ internal fun ProxyServerListTopBar(
109111
clipboard = clipboard,
110112
tipNotifier = tipNotifier,
111113
scope = scope,
114+
backgroundScope = backgroundScope,
112115
messages = messages,
113116
serviceOperationInProgress = serviceOperationInProgress,
114117
runProxyServiceOperation = runProxyServiceOperation,
@@ -151,6 +154,7 @@ private fun handleProxyServerListAddAction(
151154
clipboard: Clipboard,
152155
tipNotifier: AndroidToastTipNotifier,
153156
scope: CoroutineScope,
157+
backgroundScope: CoroutineScope,
154158
messages: ProxyServerListMessages,
155159
resultKey: String,
156160
) {
@@ -160,24 +164,15 @@ private fun handleProxyServerListAddAction(
160164
runCatching { qrScanner() }
161165
.onSuccess { scanText ->
162166
if (scanText.isNullOrBlank()) return@onSuccess
163-
if (
164-
installSubscriptionFromText(
165-
text = scanText,
166-
stateStore = stateStore,
167-
subscriptionFetcher = subscriptionFetcher,
168-
tipNotifier = tipNotifier,
169-
messages = messages,
170-
)
171-
) {
172-
return@onSuccess
173-
}
174-
importProxyServers(
167+
importProxyServersInBackground(
175168
text = scanText,
176169
source = ProxyServerImportSource.QrCode,
177170
groupState = groupState,
171+
stateStore = stateStore,
178172
subscriptionFetcher = subscriptionFetcher,
179173
updateAppState = updateAppState,
180174
tipNotifier = tipNotifier,
175+
backgroundScope = backgroundScope,
181176
messages = messages,
182177
)
183178
}
@@ -188,55 +183,39 @@ private fun handleProxyServerListAddAction(
188183
ProxyServerListAddAction.Clipboard -> {
189184
scope.launch {
190185
val text = clipboard.getPlainText().orEmpty()
191-
if (
192-
installSubscriptionFromText(
193-
text = text,
194-
stateStore = stateStore,
195-
subscriptionFetcher = subscriptionFetcher,
196-
tipNotifier = tipNotifier,
197-
messages = messages,
198-
)
199-
) {
200-
return@launch
201-
}
202-
importProxyServers(
186+
importProxyServersInBackground(
203187
text = text,
204188
source = ProxyServerImportSource.Clipboard,
205189
groupState = groupState,
190+
stateStore = stateStore,
206191
subscriptionFetcher = subscriptionFetcher,
207192
updateAppState = updateAppState,
208193
tipNotifier = tipNotifier,
194+
backgroundScope = backgroundScope,
209195
messages = messages,
210196
)
211197
}
212198
}
213199

214200
ProxyServerListAddAction.File -> {
215201
scope.launch {
216-
runCatching {
217-
proxyServerImportFileUseCase.readText()?.let { text ->
218-
if (
219-
installSubscriptionFromText(
220-
text = text,
202+
runCatching { proxyServerImportFileUseCase.readText() }
203+
.onSuccess { text ->
204+
text?.let {
205+
importProxyServersInBackground(
206+
text = it,
207+
source = ProxyServerImportSource.File,
208+
groupState = groupState,
221209
stateStore = stateStore,
222210
subscriptionFetcher = subscriptionFetcher,
211+
updateAppState = updateAppState,
223212
tipNotifier = tipNotifier,
213+
backgroundScope = backgroundScope,
224214
messages = messages,
225215
)
226-
) {
227-
return@let
228216
}
229-
importProxyServers(
230-
text = text,
231-
source = ProxyServerImportSource.File,
232-
groupState = groupState,
233-
subscriptionFetcher = subscriptionFetcher,
234-
updateAppState = updateAppState,
235-
tipNotifier = tipNotifier,
236-
messages = messages,
237-
)
238217
}
239-
}.onFailure { error -> tipNotifier.showError(error) }
218+
.onFailure { error -> tipNotifier.showError(error) }
240219
}
241220
}
242221

@@ -260,6 +239,43 @@ private fun handleProxyServerListAddAction(
260239
}
261240
}
262241

242+
private fun importProxyServersInBackground(
243+
text: String,
244+
source: ProxyServerImportSource,
245+
groupState: ProxyServerListGroups,
246+
stateStore: AndroidAppStateStore,
247+
subscriptionFetcher: AndroidSubscriptionFetcher,
248+
updateAppState: ((AppState) -> AppState) -> Unit,
249+
tipNotifier: AndroidToastTipNotifier,
250+
backgroundScope: CoroutineScope,
251+
messages: ProxyServerListMessages,
252+
) {
253+
backgroundScope.launch {
254+
runCatching {
255+
if (
256+
installSubscriptionFromText(
257+
text = text,
258+
stateStore = stateStore,
259+
subscriptionFetcher = subscriptionFetcher,
260+
tipNotifier = tipNotifier,
261+
messages = messages,
262+
)
263+
) {
264+
return@runCatching
265+
}
266+
importProxyServers(
267+
text = text,
268+
source = source,
269+
groupState = groupState,
270+
subscriptionFetcher = subscriptionFetcher,
271+
updateAppState = updateAppState,
272+
tipNotifier = tipNotifier,
273+
messages = messages,
274+
)
275+
}.onFailure { error -> tipNotifier.showError(error) }
276+
}
277+
}
278+
263279
private suspend fun installSubscriptionFromText(
264280
text: String,
265281
stateStore: AndroidAppStateStore,
@@ -335,6 +351,7 @@ private fun handleProxyServerListToolAction(
335351
clipboard: Clipboard,
336352
tipNotifier: AndroidToastTipNotifier,
337353
scope: CoroutineScope,
354+
backgroundScope: CoroutineScope,
338355
messages: ProxyServerListMessages,
339356
serviceOperationInProgress: Boolean,
340357
runProxyServiceOperation: (suspend () -> Unit) -> Unit,
@@ -389,7 +406,7 @@ private fun handleProxyServerListToolAction(
389406
updateAppState = updateAppState,
390407
subscriptionFetcher = subscriptionFetcher,
391408
tipNotifier = tipNotifier,
392-
scope = scope,
409+
backgroundScope = backgroundScope,
393410
messages = messages,
394411
)
395412
}
@@ -483,11 +500,11 @@ private fun updateSubscriptionGroups(
483500
updateAppState: ((AppState) -> AppState) -> Unit,
484501
subscriptionFetcher: AndroidSubscriptionFetcher,
485502
tipNotifier: AndroidToastTipNotifier,
486-
scope: CoroutineScope,
503+
backgroundScope: CoroutineScope,
487504
messages: ProxyServerListMessages,
488505
) {
489506
val subscriptionGroups = proxyListState.subscriptionGroups.updatableSubscriptionGroups()
490-
scope.launch {
507+
backgroundScope.launch {
491508
if (subscriptionGroups.isEmpty()) {
492509
tipNotifier.show(messages.noSubscriptionUpdates)
493510
return@launch

app/src/main/kotlin/features/resources/ResourceManagementPage.kt

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ import androidx.compose.runtime.Composable
3131
import androidx.compose.runtime.LaunchedEffect
3232
import androidx.compose.runtime.mutableStateOf
3333
import androidx.compose.runtime.remember
34-
import androidx.compose.runtime.rememberCoroutineScope
3534
import androidx.compose.ui.Alignment
3635
import androidx.compose.ui.Modifier
3736
import app.R
3837
import ui.components.BackNavigationIcon
3938
import ui.components.NavigationIcon
39+
import kotlinx.coroutines.Dispatchers
4040
import kotlinx.coroutines.launch
41+
import kotlinx.coroutines.withContext
4142
import androidx.compose.ui.res.stringResource
4243
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
4344
import top.yukonga.miuix.kmp.basic.Scaffold
@@ -69,7 +70,6 @@ fun ResourceManagementPage(
6970
val sourceOptions = settingsResourceFileSourceOptions()
7071
val tipNotifier = services.tipNotifier
7172
val topAppBarScrollBehavior = MiuixScrollBehavior()
72-
val scope = rememberCoroutineScope()
7373
var status by remember { mutableStateOf(ResourceFilesStatus()) }
7474
var updating by remember { mutableStateOf(false) }
7575
val showCustomResourceFileDialog = remember { mutableStateOf(false) }
@@ -89,22 +89,29 @@ fun ResourceManagementPage(
8989
successMessage: String?,
9090
failureStatusCustomResourceFiles: (() -> List<CustomResourceFileState>)? = null,
9191
) {
92-
scope.launch {
93-
updating = true
92+
updating = true
93+
services.appScope.launch {
9494
try {
9595
action()?.let {
96-
status = it
96+
withContext(Dispatchers.Main.immediate) {
97+
status = it
98+
}
9799
successMessage?.let { message -> tipNotifier.show(message) }
98100
}
99101
} catch (error: Throwable) {
100102
failureStatusCustomResourceFiles?.let { customResourceFiles ->
101103
runCatching {
102-
status = resourceFileUseCase.status(customResourceFiles())
104+
val failureStatus = resourceFileUseCase.status(customResourceFiles())
105+
withContext(Dispatchers.Main.immediate) {
106+
status = failureStatus
107+
}
103108
}
104109
}
105110
tipNotifier.showError(error)
106111
} finally {
107-
updating = false
112+
withContext(Dispatchers.Main.immediate) {
113+
updating = false
114+
}
108115
}
109116
}
110117
}

0 commit comments

Comments
 (0)