feat: run EventSync in background by scoping it to AccountViewModel
Move EventSyncViewModel from a screen-scoped ViewModel to a plain class held by AccountViewModel (activity-scoped). The sync job now runs on AccountViewModel's viewModelScope, so it continues when the user navigates away. The back button no longer cancels the sync. https://claude.ai/code/session_01U8qF9mK4UBvXsXP1zNMXfX
This commit is contained in:
+2
@@ -79,6 +79,7 @@ import com.vitorpamplona.amethyst.ui.note.showAmount
|
|||||||
import com.vitorpamplona.amethyst.ui.note.showAmountInteger
|
import com.vitorpamplona.amethyst.ui.note.showAmountInteger
|
||||||
import com.vitorpamplona.amethyst.ui.screen.UiSettingsState
|
import com.vitorpamplona.amethyst.ui.screen.UiSettingsState
|
||||||
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CombinedZap
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CombinedZap
|
||||||
|
import com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.eventsync.EventSyncViewModel
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
import com.vitorpamplona.amethyst.ui.tor.TorSettingsFlow
|
||||||
import com.vitorpamplona.amethyst.ui.tor.TorType
|
import com.vitorpamplona.amethyst.ui.tor.TorType
|
||||||
@@ -175,6 +176,7 @@ class AccountViewModel(
|
|||||||
val toastManager = ToastManager()
|
val toastManager = ToastManager()
|
||||||
val broadcastTracker = BroadcastTracker()
|
val broadcastTracker = BroadcastTracker()
|
||||||
val feedStates = AccountFeedContentStates(account, viewModelScope)
|
val feedStates = AccountFeedContentStates(account, viewModelScope)
|
||||||
|
val eventSyncViewModel = EventSyncViewModel(account, viewModelScope)
|
||||||
|
|
||||||
val tempManualPaymentCache = LruCache<String, List<ZapPaymentHandler.Payable>>(5)
|
val tempManualPaymentCache = LruCache<String, List<ZapPaymentHandler.Payable>>(5)
|
||||||
|
|
||||||
|
|||||||
+2
-7
@@ -48,7 +48,6 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.vitorpamplona.amethyst.R
|
import com.vitorpamplona.amethyst.R
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
import com.vitorpamplona.amethyst.ui.navigation.navs.INav
|
||||||
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
import com.vitorpamplona.amethyst.ui.navigation.topbars.TopBarWithBackButton
|
||||||
@@ -60,8 +59,7 @@ fun EventSyncScreen(
|
|||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
nav: INav,
|
nav: INav,
|
||||||
) {
|
) {
|
||||||
val syncViewModel: EventSyncViewModel =
|
val syncViewModel = accountViewModel.eventSyncViewModel
|
||||||
viewModel(factory = EventSyncViewModel.Factory(accountViewModel.account))
|
|
||||||
|
|
||||||
val syncState by syncViewModel.syncState.collectAsStateWithLifecycle()
|
val syncState by syncViewModel.syncState.collectAsStateWithLifecycle()
|
||||||
val isMobileOrMetered by accountViewModel.settings.isMobileOrMeteredConnection.collectAsStateWithLifecycle()
|
val isMobileOrMetered by accountViewModel.settings.isMobileOrMeteredConnection.collectAsStateWithLifecycle()
|
||||||
@@ -70,10 +68,7 @@ fun EventSyncScreen(
|
|||||||
topBar = {
|
topBar = {
|
||||||
TopBarWithBackButton(
|
TopBarWithBackButton(
|
||||||
caption = stringRes(R.string.event_sync_title),
|
caption = stringRes(R.string.event_sync_title),
|
||||||
popBack = {
|
popBack = nav::popBack,
|
||||||
syncViewModel.cancel()
|
|
||||||
nav.popBack()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
) { padding ->
|
) { padding ->
|
||||||
|
|||||||
+7
-12
@@ -21,15 +21,13 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.eventsync
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn.relays.eventsync
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.ViewModelProvider
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
|
import com.vitorpamplona.quartz.nip01Core.relay.client.reqs.IRequestListener
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
|
import com.vitorpamplona.quartz.nip01Core.relay.client.single.newSubId
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
import com.vitorpamplona.quartz.nip01Core.relay.normalizer.NormalizedRelayUrl
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
@@ -47,10 +45,14 @@ import kotlinx.coroutines.withTimeoutOrNull
|
|||||||
*
|
*
|
||||||
* The procedure iterates over every relay URL in LocalCache.relayHints.relayDB.
|
* The procedure iterates over every relay URL in LocalCache.relayHints.relayDB.
|
||||||
* Events are deduplicated per destination to avoid redundant sends.
|
* Events are deduplicated per destination to avoid redundant sends.
|
||||||
|
*
|
||||||
|
* Scoped to the AccountViewModel so the sync continues in the background while
|
||||||
|
* the user navigates to other screens or away from the app.
|
||||||
*/
|
*/
|
||||||
class EventSyncViewModel(
|
class EventSyncViewModel(
|
||||||
val account: Account,
|
val account: Account,
|
||||||
) : ViewModel() {
|
private val scope: CoroutineScope,
|
||||||
|
) {
|
||||||
sealed class SyncState {
|
sealed class SyncState {
|
||||||
object Idle : SyncState()
|
object Idle : SyncState()
|
||||||
|
|
||||||
@@ -81,7 +83,7 @@ class EventSyncViewModel(
|
|||||||
fun start() {
|
fun start() {
|
||||||
if (_syncState.value is SyncState.Running) return
|
if (_syncState.value is SyncState.Running) return
|
||||||
syncJob =
|
syncJob =
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
runSync()
|
runSync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,11 +278,4 @@ class EventSyncViewModel(
|
|||||||
account.client.close(subId)
|
account.client.close(subId)
|
||||||
done.close()
|
done.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
class Factory(
|
|
||||||
val account: Account,
|
|
||||||
) : ViewModelProvider.Factory {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = EventSyncViewModel(account) as T
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user