From 075c60a3ab0df8eba53a8d8034de039b27d45399 Mon Sep 17 00:00:00 2001 From: DatLag Date: Mon, 6 May 2024 21:58:44 +0200 Subject: [PATCH] prepare new trace repository --- .../datlag/aniflow/module/NetworkModule.kt | 13 ++- .../navigation/screen/home/HomeComponent.kt | 5 ++ .../ui/navigation/screen/home/HomeScreen.kt | 24 +++++ .../screen/home/HomeScreenComponent.kt | 16 ++++ .../datlag/aniflow/trace/TraceRepository.kt | 53 +++++++++++ .../datlag/aniflow/trace/TraceStateMachine.kt | 89 ------------------- 6 files changed, 104 insertions(+), 96 deletions(-) create mode 100644 trace/src/commonMain/kotlin/dev/datlag/aniflow/trace/TraceRepository.kt delete mode 100644 trace/src/commonMain/kotlin/dev/datlag/aniflow/trace/TraceStateMachine.kt diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/module/NetworkModule.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/module/NetworkModule.kt index 9ad4636..62efb3c 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/module/NetworkModule.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/module/NetworkModule.kt @@ -32,7 +32,7 @@ import dev.datlag.aniflow.model.safeFirstOrNull import dev.datlag.aniflow.other.UserHelper import dev.datlag.aniflow.settings.Settings import dev.datlag.aniflow.trace.Trace -import dev.datlag.aniflow.trace.TraceStateMachine +import dev.datlag.aniflow.trace.TraceRepository import dev.datlag.tooling.async.suspendCatching import io.github.aakira.napier.Napier import kotlinx.coroutines.flow.map @@ -113,12 +113,6 @@ data object NetworkModule { baseUrl("https://api.trace.moe/") }.create() } - bindProvider { - TraceStateMachine( - trace = instance(), - crashlytics = nullableFirebaseInstance()?.crashlytics - ) - } bindSingleton { val appSettings = instance() @@ -169,5 +163,10 @@ data object NetworkModule { fallbackClient = instance(Constants.AniList.FALLBACK_APOLLO_CLIENT) ) } + bindSingleton { + TraceRepository( + trace = instance(), + ) + } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeComponent.kt index d3911ee..df11915 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeComponent.kt @@ -5,6 +5,7 @@ import dev.datlag.aniflow.anilist.TrendingRepository import dev.datlag.aniflow.anilist.model.Medium import dev.datlag.aniflow.anilist.state.CollectionState import dev.datlag.aniflow.anilist.type.MediaType +import dev.datlag.aniflow.trace.TraceRepository import dev.datlag.aniflow.ui.navigation.Component import kotlinx.coroutines.flow.Flow @@ -16,9 +17,13 @@ interface HomeComponent : Component { val popularNow: Flow val popularNext: Flow + val traceState: Flow + fun viewProfile() fun viewAnime() fun viewManga() fun details(medium: Medium) + fun trace(byteArray: ByteArray) + fun clearTrace() } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreen.kt index d1ce91e..2ef4cc1 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreen.kt @@ -1,5 +1,6 @@ package dev.datlag.aniflow.ui.navigation.screen.home +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.LinearOutSlowInEasing import androidx.compose.animation.core.tween @@ -10,6 +11,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CameraEnhance import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider @@ -31,6 +34,8 @@ import dev.datlag.aniflow.anilist.type.MediaType import dev.datlag.aniflow.common.LocalPadding import dev.datlag.aniflow.common.isScrollingUp import dev.datlag.aniflow.other.StateSaver +import dev.datlag.aniflow.other.rememberImagePickerState +import dev.datlag.aniflow.trace.TraceRepository import dev.datlag.aniflow.ui.navigation.screen.component.CollapsingToolbar import dev.datlag.aniflow.ui.navigation.screen.component.HidingNavigationBar import dev.datlag.aniflow.ui.navigation.screen.home.component.AllLoadingView @@ -46,6 +51,9 @@ fun HomeScreen(component: HomeComponent) { state = appBarState ) val listState = rememberLazyListState() + val imagePicker = rememberImagePickerState { + it?.let(component::trace) + } Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), @@ -60,7 +68,23 @@ fun HomeScreen(component: HomeComponent) { ) }, floatingActionButton = { + val traceState by component.traceState.collectAsStateWithLifecycle(TraceRepository.State.None) + ExtendedFloatingActionButton( + onClick = { + imagePicker.launch() + }, + expanded = listState.isScrollingUp(), + icon = { + Icon( + imageVector = Icons.Filled.CameraEnhance, + contentDescription = null + ) + }, + text = { + Text(text = "Scan") + } + ) }, bottomBar = { HidingNavigationBar( diff --git a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreenComponent.kt b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreenComponent.kt index b418f15..52b3121 100644 --- a/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreenComponent.kt +++ b/composeApp/src/commonMain/kotlin/dev/datlag/aniflow/ui/navigation/screen/home/HomeScreenComponent.kt @@ -17,6 +17,7 @@ import dev.datlag.aniflow.common.onRender import dev.datlag.aniflow.model.coroutines.Executor import dev.datlag.aniflow.other.StateSaver import dev.datlag.aniflow.settings.Settings +import dev.datlag.aniflow.trace.TraceRepository import dev.datlag.tooling.decompose.ioScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted @@ -79,6 +80,13 @@ class HomeScreenComponent( initialValue = CollectionState.None ) + private val traceRepository by instance() + override val traceState: Flow = traceRepository.response + + init { + traceRepository.clear() + } + @Composable override fun render() { val haze = remember { HazeState() } @@ -117,4 +125,12 @@ class HomeScreenComponent( override fun details(medium: Medium) { onMediumDetails(medium) } + + override fun trace(byteArray: ByteArray) { + traceRepository.search(byteArray) + } + + override fun clearTrace() { + traceRepository.clear() + } } \ No newline at end of file diff --git a/trace/src/commonMain/kotlin/dev/datlag/aniflow/trace/TraceRepository.kt b/trace/src/commonMain/kotlin/dev/datlag/aniflow/trace/TraceRepository.kt new file mode 100644 index 0000000..5ff5893 --- /dev/null +++ b/trace/src/commonMain/kotlin/dev/datlag/aniflow/trace/TraceRepository.kt @@ -0,0 +1,53 @@ +package dev.datlag.aniflow.trace + +import dev.datlag.aniflow.model.CatchResult +import dev.datlag.aniflow.trace.model.SearchResponse +import kotlinx.coroutines.flow.* +import kotlinx.serialization.Serializable + +class TraceRepository( + private val trace: Trace +) { + + private val byteArray = MutableStateFlow(null) + val response: Flow = byteArray.transform { + return@transform if (it == null || it.isEmpty()) { + emit(State.None) + } else { + emit( + State.fromResponse( + CatchResult.repeat(2) { + trace.search(it) + }.asNullableSuccess() + ) + ) + } + } + + fun clear() = byteArray.update { null } + fun search(array: ByteArray) = byteArray.update { array } + + @Serializable + sealed interface State { + @Serializable + data object None : State + + @Serializable + data class Success( + val response: SearchResponse, + ) : State + + @Serializable + data object Error : State + + companion object { + fun fromResponse(response: SearchResponse?): State { + return if (response == null || response.isError) { + Error + } else { + Success(response) + } + } + } + } +} \ No newline at end of file diff --git a/trace/src/commonMain/kotlin/dev/datlag/aniflow/trace/TraceStateMachine.kt b/trace/src/commonMain/kotlin/dev/datlag/aniflow/trace/TraceStateMachine.kt deleted file mode 100644 index 09e8c5b..0000000 --- a/trace/src/commonMain/kotlin/dev/datlag/aniflow/trace/TraceStateMachine.kt +++ /dev/null @@ -1,89 +0,0 @@ -package dev.datlag.aniflow.trace - -import com.freeletics.flowredux.dsl.FlowReduxStateMachine -import dev.datlag.aniflow.firebase.FirebaseFactory -import dev.datlag.aniflow.model.CatchResult -import dev.datlag.aniflow.trace.model.SearchResponse -import io.ktor.client.request.forms.* -import io.ktor.utils.io.* -import kotlinx.coroutines.ExperimentalCoroutinesApi - -@OptIn(ExperimentalCoroutinesApi::class) -class TraceStateMachine( - private val trace: Trace, - private val crashlytics: FirebaseFactory.Crashlytics? -) : FlowReduxStateMachine( - initialState = State.Waiting -) { - - init { - spec { - inState { - on { action, state -> - state.override { State.Loading(action.image) } - } - } - inState { - onEnter { state -> - val response = CatchResult.repeat(2) { - val result = trace.search(state.snapshot.image) - - if (result.isError) { - throw IllegalStateException("Result is Error") - } else { - result - } - }.mapSuccess { - State.Success(it) - } - - state.override { - response.asSuccess { - crashlytics?.log(it) - - State.Error - } - } - } - } - inState { - on { action, state -> - state.override { State.Loading(action.image) } - } - } - inState { - on { action, state -> - state.override { State.Loading(action.image) } - } - } - } - } - - sealed interface State { - - val isWaiting: Boolean - get() = this is Waiting - - val isLoading: Boolean - get() = this is Loading - - val isSuccess: Boolean - get() = this is Success - - data object Waiting : State - - class Loading( - internal val image: ByteArray - ) : State - - data class Success( - val response: SearchResponse - ) : State - - data object Error : State - } - - sealed interface Action { - class Load(internal val image: ByteArray) : Action - } -} \ No newline at end of file