diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/RememberGlobally.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/RememberGlobally.kt index 6f0b3d004..bc15773c8 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/RememberGlobally.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/tools/RememberGlobally.kt @@ -3,11 +3,17 @@ package com.hippo.ehviewer.ui.tools import androidx.compose.runtime.Composable import androidx.compose.runtime.DisallowComposableCalls import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.MutableState import androidx.compose.runtime.currentCompositeKeyHash import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState +import androidx.datastore.dataStoreFile +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import androidx.datastore.preferences.core.byteArrayPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.emptyPreferences import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.compose.viewModel @@ -15,21 +21,32 @@ import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import kotlinx.serialization.cbor.Cbor +import kotlinx.serialization.decodeFromByteArray +import kotlinx.serialization.encodeToByteArray +import splitties.init.appCtx + +val dataStoreScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) +val dataStore = PreferenceDataStoreFactory.create { appCtx.dataStoreFile("Remembered") } +val dataStateFlow = dataStore.data.stateIn(dataStoreScope, SharingStarted.Eagerly, emptyPreferences()) class StateMapViewModel : ViewModel() { val statesMap = mutableMapOf>() } @Composable -inline fun rememberInVM( +inline fun rememberInVM( vararg inputs: Any?, crossinline init: @DisallowComposableCalls ViewModel.() -> T, ) = with(viewModel()) { val key = currentCompositeKeyHash remember(*inputs) { val states = statesMap[key] ?: ArrayDeque().also { statesMap[key] = it } - @Suppress("UNCHECKED_CAST") states.removeLastOrNull() as T? ?: init() }.also { value -> val valueState by rememberUpdatedState(value) @@ -41,6 +58,30 @@ inline fun rememberInVM( } } +@Composable +inline fun > rememberInDataStore( + key: String, + crossinline defaultValue: @DisallowComposableCalls () -> MutableState, +) = with(dataStateFlow) { + remember { + val keyObj = byteArrayPreferencesKey(key) + value[keyObj]?.let { bytes -> + mutableStateOf(Cbor.decodeFromByteArray(bytes)) + } ?: defaultValue() + }.also { value -> + DisposableEffect(key) { + onDispose { + dataStoreScope.launch { + val keyObj = byteArrayPreferencesKey(key) + dataStore.edit { p -> + p[keyObj] = Cbor.encodeToByteArray(value.value) + } + } + } + } + } +} + @Composable fun launchInVM( context: CoroutineContext = EmptyCoroutineContext,