[dev QoL] Added AndroidStudio previews for [presentation.history] namespace (#10012)
* Added display preview for HistoryDialogs * Added preview with provider for each branch for HistoryItem * Added previews for HistoryScreen Created in-memory preferences construct for when its needed at top-level injection * Fixed ktlint violations
This commit is contained in:
parent
0be7ac5871
commit
447bcb28ef
7 changed files with 348 additions and 1 deletions
|
@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.presentation.components.AppBar
|
import eu.kanade.presentation.components.AppBar
|
||||||
import eu.kanade.presentation.components.AppBarActions
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
|
@ -18,13 +19,16 @@ import eu.kanade.presentation.components.AppBarTitle
|
||||||
import eu.kanade.presentation.components.RelativeDateHeader
|
import eu.kanade.presentation.components.RelativeDateHeader
|
||||||
import eu.kanade.presentation.components.SearchToolbar
|
import eu.kanade.presentation.components.SearchToolbar
|
||||||
import eu.kanade.presentation.history.components.HistoryItem
|
import eu.kanade.presentation.history.components.HistoryItem
|
||||||
|
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
||||||
|
import tachiyomi.core.preference.InMemoryPreferenceStore
|
||||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||||
|
import tachiyomi.presentation.core.util.ThemePreviews
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
@ -37,6 +41,7 @@ fun HistoryScreen(
|
||||||
onClickCover: (mangaId: Long) -> Unit,
|
onClickCover: (mangaId: Long) -> Unit,
|
||||||
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
onClickResume: (mangaId: Long, chapterId: Long) -> Unit,
|
||||||
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
||||||
|
preferences: UiPreferences = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
|
@ -82,6 +87,7 @@ fun HistoryScreen(
|
||||||
onClickCover = { history -> onClickCover(history.mangaId) },
|
onClickCover = { history -> onClickCover(history.mangaId) },
|
||||||
onClickResume = { history -> onClickResume(history.mangaId, history.chapterId) },
|
onClickResume = { history -> onClickResume(history.mangaId, history.chapterId) },
|
||||||
onClickDelete = { item -> onDialogChange(HistoryScreenModel.Dialog.Delete(item)) },
|
onClickDelete = { item -> onDialogChange(HistoryScreenModel.Dialog.Delete(item)) },
|
||||||
|
preferences = preferences,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +101,7 @@ private fun HistoryScreenContent(
|
||||||
onClickCover: (HistoryWithRelations) -> Unit,
|
onClickCover: (HistoryWithRelations) -> Unit,
|
||||||
onClickResume: (HistoryWithRelations) -> Unit,
|
onClickResume: (HistoryWithRelations) -> Unit,
|
||||||
onClickDelete: (HistoryWithRelations) -> Unit,
|
onClickDelete: (HistoryWithRelations) -> Unit,
|
||||||
preferences: UiPreferences = Injekt.get(),
|
preferences: UiPreferences,
|
||||||
) {
|
) {
|
||||||
val relativeTime = remember { preferences.relativeTime().get() }
|
val relativeTime = remember { preferences.relativeTime().get() }
|
||||||
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
|
val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) }
|
||||||
|
@ -141,3 +147,32 @@ sealed interface HistoryUiModel {
|
||||||
data class Header(val date: Date) : HistoryUiModel
|
data class Header(val date: Date) : HistoryUiModel
|
||||||
data class Item(val item: HistoryWithRelations) : HistoryUiModel
|
data class Item(val item: HistoryWithRelations) : HistoryUiModel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ThemePreviews
|
||||||
|
@Composable
|
||||||
|
internal fun HistoryScreenPreviews(
|
||||||
|
@PreviewParameter(HistoryScreenModelStateProvider::class)
|
||||||
|
historyState: HistoryScreenModel.State,
|
||||||
|
) {
|
||||||
|
TachiyomiTheme {
|
||||||
|
HistoryScreen(
|
||||||
|
state = historyState,
|
||||||
|
snackbarHostState = SnackbarHostState(),
|
||||||
|
onSearchQueryChange = {},
|
||||||
|
onClickCover = {},
|
||||||
|
onClickResume = { _, _ -> run {} },
|
||||||
|
onDialogChange = {},
|
||||||
|
preferences = UiPreferences(
|
||||||
|
InMemoryPreferenceStore(
|
||||||
|
sequenceOf(
|
||||||
|
InMemoryPreferenceStore.InMemoryPreference(
|
||||||
|
key = "relative_time_v2",
|
||||||
|
data = false,
|
||||||
|
defaultValue = false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
package eu.kanade.presentation.history
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
||||||
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
|
import tachiyomi.domain.manga.model.MangaCover
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
import java.util.Date
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class HistoryScreenModelStateProvider : PreviewParameterProvider<HistoryScreenModel.State> {
|
||||||
|
|
||||||
|
private val multiPage = HistoryScreenModel.State(
|
||||||
|
searchQuery = null,
|
||||||
|
list =
|
||||||
|
listOf(HistoryUiModelExamples.headerToday)
|
||||||
|
.asSequence()
|
||||||
|
.plus(HistoryUiModelExamples.items().take(3))
|
||||||
|
.plus(HistoryUiModelExamples.header { it.minus(1, ChronoUnit.DAYS) })
|
||||||
|
.plus(HistoryUiModelExamples.items().take(1))
|
||||||
|
.plus(HistoryUiModelExamples.header { it.minus(2, ChronoUnit.DAYS) })
|
||||||
|
.plus(HistoryUiModelExamples.items().take(7))
|
||||||
|
.toList(),
|
||||||
|
dialog = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val shortRecent = HistoryScreenModel.State(
|
||||||
|
searchQuery = null,
|
||||||
|
list = listOf(
|
||||||
|
HistoryUiModelExamples.headerToday,
|
||||||
|
HistoryUiModelExamples.items().first(),
|
||||||
|
),
|
||||||
|
dialog = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val shortFuture = HistoryScreenModel.State(
|
||||||
|
searchQuery = null,
|
||||||
|
list = listOf(
|
||||||
|
HistoryUiModelExamples.headerTomorrow,
|
||||||
|
HistoryUiModelExamples.items().first(),
|
||||||
|
),
|
||||||
|
dialog = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val empty = HistoryScreenModel.State(
|
||||||
|
searchQuery = null,
|
||||||
|
list = listOf(),
|
||||||
|
dialog = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val loadingWithSearchQuery = HistoryScreenModel.State(
|
||||||
|
searchQuery = "Example Search Query",
|
||||||
|
)
|
||||||
|
|
||||||
|
private val loading = HistoryScreenModel.State(
|
||||||
|
searchQuery = null,
|
||||||
|
list = null,
|
||||||
|
dialog = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
override val values: Sequence<HistoryScreenModel.State> = sequenceOf(
|
||||||
|
multiPage,
|
||||||
|
shortRecent,
|
||||||
|
shortFuture,
|
||||||
|
empty,
|
||||||
|
loadingWithSearchQuery,
|
||||||
|
loading,
|
||||||
|
)
|
||||||
|
|
||||||
|
private object HistoryUiModelExamples {
|
||||||
|
val headerToday = header()
|
||||||
|
val headerTomorrow =
|
||||||
|
HistoryUiModel.Header(Date.from(Instant.now().plus(1, ChronoUnit.DAYS)))
|
||||||
|
|
||||||
|
fun header(instantBuilder: (Instant) -> Instant = { it }) =
|
||||||
|
HistoryUiModel.Header(Date.from(instantBuilder(Instant.now())))
|
||||||
|
|
||||||
|
fun items() = sequence {
|
||||||
|
var count = 1
|
||||||
|
while (true) {
|
||||||
|
yield(randItem { it.copy(title = "Example Title $count") })
|
||||||
|
count += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun randItem(historyBuilder: (HistoryWithRelations) -> HistoryWithRelations = { it }) =
|
||||||
|
HistoryUiModel.Item(
|
||||||
|
historyBuilder(
|
||||||
|
HistoryWithRelations(
|
||||||
|
id = Random.nextLong(),
|
||||||
|
chapterId = Random.nextLong(),
|
||||||
|
mangaId = Random.nextLong(),
|
||||||
|
title = "Test Title",
|
||||||
|
chapterNumber = Random.nextDouble(),
|
||||||
|
readAt = Date.from(Instant.now()),
|
||||||
|
readDuration = Random.nextLong(),
|
||||||
|
coverData = MangaCover(
|
||||||
|
mangaId = Random.nextLong(),
|
||||||
|
sourceId = Random.nextLong(),
|
||||||
|
isMangaFavorite = Random.nextBoolean(),
|
||||||
|
url = "https://example.com/cover.png",
|
||||||
|
lastModified = Random.nextLong(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package eu.kanade.presentation.history
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
object HistoryUiModelProviders {
|
||||||
|
|
||||||
|
class HeadNow : PreviewParameterProvider<HistoryUiModel> {
|
||||||
|
override val values: Sequence<HistoryUiModel> =
|
||||||
|
sequenceOf(HistoryUiModel.Header(Date.from(Instant.now())))
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,9 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
import tachiyomi.presentation.core.util.ThemePreviews
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HistoryDeleteDialog(
|
fun HistoryDeleteDialog(
|
||||||
|
@ -101,3 +103,14 @@ fun HistoryDeleteAllDialog(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ThemePreviews
|
||||||
|
@Composable
|
||||||
|
internal fun HistoryDeleteDialogPreview() {
|
||||||
|
TachiyomiTheme {
|
||||||
|
HistoryDeleteDialog(
|
||||||
|
onDismissRequest = {},
|
||||||
|
onDelete = { _ -> run {} },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,13 +19,16 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
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.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.manga.components.MangaCover
|
import eu.kanade.presentation.manga.components.MangaCover
|
||||||
|
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||||
import eu.kanade.presentation.util.formatChapterNumber
|
import eu.kanade.presentation.util.formatChapterNumber
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
|
import tachiyomi.presentation.core.util.ThemePreviews
|
||||||
|
|
||||||
private val HISTORY_ITEM_HEIGHT = 96.dp
|
private val HISTORY_ITEM_HEIGHT = 96.dp
|
||||||
|
|
||||||
|
@ -87,3 +90,19 @@ fun HistoryItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ThemePreviews
|
||||||
|
@Composable
|
||||||
|
internal fun HistoryItemPreviews(
|
||||||
|
@PreviewParameter(HistoryWithRelationsProvider::class)
|
||||||
|
historyWithRelations: HistoryWithRelations,
|
||||||
|
) {
|
||||||
|
TachiyomiTheme {
|
||||||
|
HistoryItem(
|
||||||
|
history = historyWithRelations,
|
||||||
|
onClickCover = {},
|
||||||
|
onClickResume = {},
|
||||||
|
onClickDelete = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package eu.kanade.presentation.history.components
|
||||||
|
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
internal class HistoryWithRelationsProvider : PreviewParameterProvider<HistoryWithRelations> {
|
||||||
|
|
||||||
|
private val simple = HistoryWithRelations(
|
||||||
|
id = 1L,
|
||||||
|
chapterId = 2L,
|
||||||
|
mangaId = 3L,
|
||||||
|
title = "Test Title",
|
||||||
|
chapterNumber = 10.2,
|
||||||
|
readAt = Date(1697247357L),
|
||||||
|
readDuration = 123L,
|
||||||
|
coverData = tachiyomi.domain.manga.model.MangaCover(
|
||||||
|
mangaId = 3L,
|
||||||
|
sourceId = 4L,
|
||||||
|
isMangaFavorite = false,
|
||||||
|
url = "https://example.com/cover.png",
|
||||||
|
lastModified = 5L,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private val historyWithoutReadAt = HistoryWithRelations(
|
||||||
|
id = 1L,
|
||||||
|
chapterId = 2L,
|
||||||
|
mangaId = 3L,
|
||||||
|
title = "Test Title",
|
||||||
|
chapterNumber = 10.2,
|
||||||
|
readAt = null,
|
||||||
|
readDuration = 123L,
|
||||||
|
coverData = tachiyomi.domain.manga.model.MangaCover(
|
||||||
|
mangaId = 3L,
|
||||||
|
sourceId = 4L,
|
||||||
|
isMangaFavorite = false,
|
||||||
|
url = "https://example.com/cover.png",
|
||||||
|
lastModified = 5L,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
private val historyWithNegativeChapterNumber = HistoryWithRelations(
|
||||||
|
id = 1L,
|
||||||
|
chapterId = 2L,
|
||||||
|
mangaId = 3L,
|
||||||
|
title = "Test Title",
|
||||||
|
chapterNumber = -2.0,
|
||||||
|
readAt = Date(1697247357L),
|
||||||
|
readDuration = 123L,
|
||||||
|
coverData = tachiyomi.domain.manga.model.MangaCover(
|
||||||
|
mangaId = 3L,
|
||||||
|
sourceId = 4L,
|
||||||
|
isMangaFavorite = false,
|
||||||
|
url = "https://example.com/cover.png",
|
||||||
|
lastModified = 5L,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
override val values: Sequence<HistoryWithRelations>
|
||||||
|
get() = sequenceOf(simple, historyWithoutReadAt, historyWithNegativeChapterNumber)
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package tachiyomi.core.preference
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local-copy implementation of PreferenceStore mostly for test and preview purposes
|
||||||
|
*/
|
||||||
|
class InMemoryPreferenceStore(
|
||||||
|
private val initialPreferences: Sequence<InMemoryPreference<*>> = sequenceOf(),
|
||||||
|
) : PreferenceStore {
|
||||||
|
|
||||||
|
private val preferences: Map<String, Preference<*>> =
|
||||||
|
initialPreferences.toList().associateBy { it.key() }
|
||||||
|
|
||||||
|
override fun getString(key: String, defaultValue: String): Preference<String> {
|
||||||
|
val default = InMemoryPreference(key, null, defaultValue)
|
||||||
|
val data: String? = preferences[key]?.get() as? String
|
||||||
|
return if (data == null) default else InMemoryPreference(key, data, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLong(key: String, defaultValue: Long): Preference<Long> {
|
||||||
|
val default = InMemoryPreference(key, null, defaultValue)
|
||||||
|
val data: Long? = preferences[key]?.get() as? Long
|
||||||
|
return if (data == null) default else InMemoryPreference(key, data, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getInt(key: String, defaultValue: Int): Preference<Int> {
|
||||||
|
val default = InMemoryPreference(key, null, defaultValue)
|
||||||
|
val data: Int? = preferences[key]?.get() as? Int
|
||||||
|
return if (data == null) default else InMemoryPreference(key, data, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFloat(key: String, defaultValue: Float): Preference<Float> {
|
||||||
|
val default = InMemoryPreference(key, null, defaultValue)
|
||||||
|
val data: Float? = preferences[key]?.get() as? Float
|
||||||
|
return if (data == null) default else InMemoryPreference(key, data, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getBoolean(key: String, defaultValue: Boolean): Preference<Boolean> {
|
||||||
|
val default = InMemoryPreference(key, null, defaultValue)
|
||||||
|
val data: Boolean? = preferences[key]?.get() as? Boolean
|
||||||
|
return if (data == null) default else InMemoryPreference(key, data, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getStringSet(key: String, defaultValue: Set<String>): Preference<Set<String>> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T> getObject(
|
||||||
|
key: String,
|
||||||
|
defaultValue: T,
|
||||||
|
serializer: (T) -> String,
|
||||||
|
deserializer: (String) -> T,
|
||||||
|
): Preference<T> {
|
||||||
|
val default = InMemoryPreference(key, null, defaultValue)
|
||||||
|
val data: T? = preferences[key]?.get() as? T
|
||||||
|
return if (data == null) default else InMemoryPreference<T>(key, data, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAll(): Map<String, *> {
|
||||||
|
return preferences
|
||||||
|
}
|
||||||
|
|
||||||
|
class InMemoryPreference<T>(
|
||||||
|
private val key: String,
|
||||||
|
private var data: T?,
|
||||||
|
private val defaultValue: T,
|
||||||
|
) : Preference<T> {
|
||||||
|
override fun key(): String = key
|
||||||
|
|
||||||
|
override fun get(): T = data ?: defaultValue()
|
||||||
|
|
||||||
|
override fun isSet(): Boolean = data != null
|
||||||
|
|
||||||
|
override fun delete() {
|
||||||
|
data = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun defaultValue(): T = defaultValue
|
||||||
|
|
||||||
|
override fun changes(): Flow<T> = flow { data }
|
||||||
|
|
||||||
|
override fun stateIn(scope: CoroutineScope): StateFlow<T> {
|
||||||
|
return changes().stateIn(scope, SharingStarted.Eagerly, get())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun set(value: T) {
|
||||||
|
data = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in a new issue