Preference Screen (WIP)

This commit is contained in:
shabinder 2021-06-25 12:36:29 +05:30
parent c3c71813c2
commit 08aa6d0f18
17 changed files with 431 additions and 49 deletions

View File

@ -126,10 +126,10 @@ actual fun DonationDialog(
horizontalArrangement = Arrangement.SpaceEvenly, horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.padding(horizontal = 4.dp).fillMaxWidth() modifier = Modifier.padding(horizontal = 4.dp).fillMaxWidth()
) { ) {
OutlinedButton(onClick = onSnooze) { OutlinedButton(onClick = onDismiss) {
Text("Dismiss.") Text("Dismiss.")
} }
TextButton(onClick = onDismiss, colors = ButtonDefaults.buttonColors()) { TextButton(onClick = onSnooze, colors = ButtonDefaults.buttonColors()) {
Text("Remind Later!") Text("Remind Later!")
} }
} }

View File

@ -22,6 +22,7 @@ import androidx.compose.ui.graphics.Color
val colorPrimary = Color(0xFFFC5C7D) val colorPrimary = Color(0xFFFC5C7D)
val colorPrimaryDark = Color(0xFFCE1CFF) val colorPrimaryDark = Color(0xFFCE1CFF)
val colorAccent = Color(0xFF9AB3FF) val colorAccent = Color(0xFF9AB3FF)
val colorAccentVariant = Color(0xFF3457D5)
val colorRedError = Color(0xFFFF9494) val colorRedError = Color(0xFFFF9494)
val colorSuccessGreen = Color(0xFF59C351) val colorSuccessGreen = Color(0xFF59C351)
val darkBackgroundColor = Color(0xFF000000) val darkBackgroundColor = Color(0xFF000000)

View File

@ -40,9 +40,6 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@ -57,6 +54,7 @@ import com.shabinder.common.list.SpotiFlyerList
import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
import com.shabinder.common.models.methods import com.shabinder.common.models.methods
import com.shabinder.common.uikit.dialogs.DonationDialogComponent
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
@ -104,25 +102,19 @@ fun SpotiFlyerListContent(
state = listState, state = listState,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) )
// Donation Dialog Visibility // Donation Dialog Visibility
var visibilty by remember { mutableStateOf(false) } val (openDonationDialog,dismissDonationDialog,snoozeDonationDialog) = DonationDialogComponent {
DonationDialog( component.dismissDonationDialogSetOffset()
isVisible = visibilty, }
onDismiss = {
visibilty = false
},
onSnooze = {
visibilty = false
component.snoozeDonationDialog()
}
)
DownloadAllButton( DownloadAllButton(
onClick = { onClick = {
component.onDownloadAllClicked(model.trackList) component.onDownloadAllClicked(model.trackList)
// Check If we are allowed to show donation Dialog // Check If we are allowed to show donation Dialog
if (model.askForDonation) { if (model.askForDonation) {
// Show Donation Dialog // Show Donation Dialog
visibilty = true openDonationDialog()
} }
}, },
modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter) modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter)

View File

@ -17,21 +17,54 @@
package com.shabinder.common.uikit package com.shabinder.common.uikit
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
import androidx.compose.foundation.* import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.* import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.* import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Card
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Switch
import androidx.compose.material.SwitchDefaults
import androidx.compose.material.Tab
import androidx.compose.material.TabPosition
import androidx.compose.material.TabRow
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults.textFieldColors import androidx.compose.material.TextFieldDefaults.textFieldColors
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.History
import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.rounded.* import androidx.compose.material.icons.rounded.CardGiftcard
import androidx.compose.runtime.* import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material.icons.rounded.Flag
import androidx.compose.material.icons.rounded.Insights
import androidx.compose.material.icons.rounded.Share
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@ -50,11 +83,16 @@ import com.shabinder.common.main.SpotiFlyerMain
import com.shabinder.common.main.SpotiFlyerMain.HomeCategory import com.shabinder.common.main.SpotiFlyerMain.HomeCategory
import com.shabinder.common.models.DownloadRecord import com.shabinder.common.models.DownloadRecord
import com.shabinder.common.models.methods import com.shabinder.common.models.methods
import com.shabinder.common.uikit.dialogs.DonationDialogComponent
@Composable @Composable
fun SpotiFlyerMainContent(component: SpotiFlyerMain) { fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
val model by component.model.subscribeAsState() val model by component.model.subscribeAsState()
val (openDonationDialog,_,_) = DonationDialogComponent {
component.dismissDonationDialogOffset()
}
Column { Column {
SearchPanel( SearchPanel(
model.link, model.link,
@ -65,14 +103,17 @@ fun SpotiFlyerMainContent(component: SpotiFlyerMain) {
HomeTabBar( HomeTabBar(
model.selectedCategory, model.selectedCategory,
HomeCategory.values(), HomeCategory.values(),
component::selectCategory component::selectCategory,
) )
when (model.selectedCategory) { when (model.selectedCategory) {
HomeCategory.About -> AboutColumn( HomeCategory.About -> AboutColumn(
analyticsEnabled = model.isAnalyticsEnabled, analyticsEnabled = model.isAnalyticsEnabled,
donationDialogOpenEvent = { component.analytics.donationDialogVisit() }, toggleAnalytics = component::toggleAnalytics,
toggleAnalytics = component::toggleAnalytics openDonationDialog = {
component.analytics.donationDialogVisit()
openDonationDialog()
}
) )
HomeCategory.History -> HistoryColumn( HomeCategory.History -> HistoryColumn(
model.records.sortedByDescending { it.id }, model.records.sortedByDescending { it.id },
@ -98,6 +139,7 @@ fun HomeTabBar(
} }
TabRow( TabRow(
backgroundColor = transparent,
selectedTabIndex = selectedIndex, selectedTabIndex = selectedIndex,
indicator = indicator, indicator = indicator,
modifier = modifier, modifier = modifier,
@ -195,7 +237,7 @@ fun SearchPanel(
fun AboutColumn( fun AboutColumn(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
analyticsEnabled:Boolean, analyticsEnabled:Boolean,
donationDialogOpenEvent: () -> Unit, openDonationDialog: () -> Unit,
toggleAnalytics: (enabled: Boolean) -> Unit toggleAnalytics: (enabled: Boolean) -> Unit
) { ) {
@ -313,26 +355,9 @@ fun AboutColumn(
} }
} }
var isDonationDialogVisible by remember { mutableStateOf(false) }
DonationDialog(
isDonationDialogVisible,
onDismiss = {
isDonationDialogVisible = false
},
onSnooze = {
isDonationDialogVisible = false
}
)
Row( Row(
modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) modifier = modifier.fillMaxWidth().padding(vertical = 6.dp)
.clickable( .clickable(onClick = openDonationDialog),
onClick = {
isDonationDialogVisible = true
donationDialogOpenEvent()
}
),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Icon(Icons.Rounded.CardGiftcard, "Support Developer", Modifier.size(32.dp)) Icon(Icons.Rounded.CardGiftcard, "Support Developer", Modifier.size(32.dp))
@ -504,7 +529,7 @@ fun HomeCategoryTabIndicator(
) { ) {
Spacer( Spacer(
modifier.padding(horizontal = 24.dp) modifier.padding(horizontal = 24.dp)
.height(4.dp) .height(3.dp)
.background(color, RoundedCornerShape(topStartPercent = 100, topEndPercent = 100)) .background(color, RoundedCornerShape(topStartPercent = 100, topEndPercent = 100))
) )
} }

View File

@ -56,7 +56,7 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.arkivanov.decompose.extensions.compose.jetbrains.Children import com.arkivanov.decompose.extensions.compose.jetbrains.Children
import com.arkivanov.decompose.extensions.compose.jetbrains.animation.child.crossfadeScale import com.arkivanov.decompose.extensions.compose.jetbrains.animation.child.crossfadeScale
import com.arkivanov.decompose.extensions.compose.jetbrains.asState import com.arkivanov.decompose.extensions.compose.jetbrains.subscribeAsState
import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot
import com.shabinder.common.root.SpotiFlyerRoot.Child import com.shabinder.common.root.SpotiFlyerRoot.Child
import com.shabinder.common.uikit.splash.Splash import com.shabinder.common.uikit.splash.Splash
@ -125,7 +125,7 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float, topPadding: Dp = 0.d
).then(modifier) ).then(modifier)
) { ) {
val activeComponent = component.routerState.asState() val activeComponent = component.routerState.subscribeAsState()
val callBacks = component.callBacks val callBacks = component.callBacks
AppBar( AppBar(
backgroundColor = appBarColor, backgroundColor = appBarColor,

View File

@ -1 +1,33 @@
package com.shabinder.common.uikit.dialogs package com.shabinder.common.uikit.dialogs
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import com.shabinder.common.uikit.DonationDialog
typealias DonationDialogCallBacks = Triple<openAction,dismissAction,snoozeAction>
private typealias openAction = () -> Unit
private typealias dismissAction = () -> Unit
private typealias snoozeAction = () -> Unit
@Composable
fun DonationDialogComponent(onDismissExtra: () -> Unit): DonationDialogCallBacks {
var isDonationDialogVisible by remember { mutableStateOf(false) }
DonationDialog(
isDonationDialogVisible,
onSnooze = { isDonationDialogVisible = false },
onDismiss = {
isDonationDialogVisible = false
}
)
val openDonationDialog = { isDonationDialogVisible = true }
val snoozeDonationDialog = { isDonationDialogVisible = false }
val dismissDonationDialog = {
onDismissExtra()
isDonationDialogVisible = false
}
return DonationDialogCallBacks(openDonationDialog,dismissDonationDialog,snoozeDonationDialog)
}

View File

@ -62,7 +62,7 @@ interface SpotiFlyerList {
/* /*
* Snooze Donation Dialog * Snooze Donation Dialog
* */ * */
fun snoozeDonationDialog() fun dismissDonationDialogSetOffset()
interface Dependencies { interface Dependencies {
val storeFactory: StoreFactory val storeFactory: StoreFactory

View File

@ -78,7 +78,7 @@ internal class SpotiFlyerListImpl(
store.accept(Intent.RefreshTracksStatuses) store.accept(Intent.RefreshTracksStatuses)
} }
override fun snoozeDonationDialog() { override fun dismissDonationDialogSetOffset() {
preferenceManager.setDonationOffset(offset = 10) preferenceManager.setDonationOffset(offset = 10)
} }

View File

@ -59,6 +59,8 @@ interface SpotiFlyerMain {
* */ * */
suspend fun loadImage(url: String): Picture suspend fun loadImage(url: String): Picture
fun dismissDonationDialogOffset()
interface Dependencies { interface Dependencies {
val mainOutput: Consumer<Output> val mainOutput: Consumer<Output>
val storeFactory: StoreFactory val storeFactory: StoreFactory

View File

@ -82,4 +82,8 @@ internal class SpotiFlyerMainImpl(
dir.loadImage(url, 150, 150) dir.loadImage(url, 150, 150)
} }
} }
override fun dismissDonationDialogOffset() {
preferenceManager.setDonationOffset()
}
} }

View File

@ -0,0 +1,35 @@
/*
* * Copyright (c) 2021 Shabinder Singh
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
plugins {
id("android-setup")
id("multiplatform-setup")
id("multiplatform-setup-test")
id("kotlin-parcelize")
}
kotlin {
sourceSets {
commonMain {
dependencies {
implementation(project(":common:dependency-injection"))
implementation(project(":common:data-models"))
implementation(project(":common:database"))
implementation(SqlDelight.coroutineExtensions)
}
}
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ * Copyright (c) 2021 Shabinder Singh
~ * This program is free software: you can redistribute it and/or modify
~ * it under the terms of the GNU General Public License as published by
~ * the Free Software Foundation, either version 3 of the License, or
~ * (at your option) any later version.
~ *
~ * This program is distributed in the hope that it will be useful,
~ * but WITHOUT ANY WARRANTY; without even the implied warranty of
~ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ * GNU General Public License for more details.
~ *
~ * You should have received a copy of the GNU General Public License
~ * along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<manifest package="com.shabinder.common.preference"/>

View File

@ -0,0 +1,63 @@
/*
* * Copyright (c) 2021 Shabinder Singh
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.preference
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.value.Value
import com.arkivanov.mvikotlin.core.store.StoreFactory
import com.shabinder.common.di.Dir
import com.shabinder.common.di.Picture
import com.shabinder.common.di.preference.PreferenceManager
import com.shabinder.common.models.AudioQuality
import com.shabinder.common.models.Consumer
import com.shabinder.common.preference.integration.SpotiFlyerPreferenceImpl
interface SpotiFlyerPreference {
val model: Value<State>
val analytics: Analytics
fun toggleAnalytics(enabled: Boolean)
fun setDownloadDirectory(newBasePath: String)
suspend fun loadImage(url: String): Picture
interface Dependencies {
val prefOutput: Consumer<Output>
val storeFactory: StoreFactory
val dir: Dir
val preferenceManager: PreferenceManager
val preferenceAnalytics: Analytics
}
interface Analytics
sealed class Output {
object Finished : Output()
}
data class State(
val preferredQuality: AudioQuality = AudioQuality.KBPS320,
val isAnalyticsEnabled: Boolean = false
)
}
@Suppress("FunctionName") // Factory function
fun SpotiFlyerPreference(componentContext: ComponentContext, dependencies: SpotiFlyerPreference.Dependencies): SpotiFlyerPreference =
SpotiFlyerPreferenceImpl(componentContext, dependencies)

View File

@ -0,0 +1,71 @@
/*
* * Copyright (c) 2021 Shabinder Singh
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.preference.integration
import co.touchlab.stately.ensureNeverFrozen
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.value.Value
import com.shabinder.common.caching.Cache
import com.shabinder.common.di.Picture
import com.shabinder.common.di.utils.asValue
import com.shabinder.common.preference.SpotiFlyerPreference
import com.shabinder.common.preference.SpotiFlyerPreference.Dependencies
import com.shabinder.common.preference.SpotiFlyerPreference.State
import com.shabinder.common.preference.store.SpotiFlyerPreferenceStore.Intent
import com.shabinder.common.preference.store.SpotiFlyerPreferenceStoreProvider
import com.shabinder.common.preference.store.getStore
internal class SpotiFlyerPreferenceImpl(
componentContext: ComponentContext,
dependencies: Dependencies
) : SpotiFlyerPreference, ComponentContext by componentContext, Dependencies by dependencies {
init {
instanceKeeper.ensureNeverFrozen()
}
private val store =
instanceKeeper.getStore {
SpotiFlyerPreferenceStoreProvider(
storeFactory = storeFactory,
preferenceManager = preferenceManager
).provide()
}
private val cache = Cache.Builder
.newBuilder()
.maximumCacheSize(10)
.build<String, Picture>()
override val model: Value<State> = store.asValue()
override val analytics = preferenceAnalytics
override fun toggleAnalytics(enabled: Boolean) {
store.accept(Intent.ToggleAnalytics(enabled))
}
override fun setDownloadDirectory(newBasePath: String) {
preferenceManager.setDownloadDirectory(newBasePath)
}
override suspend fun loadImage(url: String): Picture {
return cache.get(url) {
dir.loadImage(url, 150, 150)
}
}
}

View File

@ -0,0 +1,37 @@
/*
* * Copyright (c) 2021 Shabinder Singh
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.preference.store
import com.arkivanov.decompose.instancekeeper.InstanceKeeper
import com.arkivanov.decompose.instancekeeper.getOrCreate
import com.arkivanov.mvikotlin.core.store.Store
fun <T : Store<*, *, *>> InstanceKeeper.getStore(key: Any, factory: () -> T): T =
getOrCreate(key) { StoreHolder(factory()) }
.store
inline fun <reified T :
Store<*, *, *>> InstanceKeeper.getStore(noinline factory: () -> T): T =
getStore(T::class, factory)
private class StoreHolder<T : Store<*, *, *>>(
val store: T
) : InstanceKeeper.Instance {
override fun onDestroy() {
store.dispose()
}
}

View File

@ -0,0 +1,29 @@
/*
* * Copyright (c) 2021 Shabinder Singh
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.preference.store
import com.arkivanov.mvikotlin.core.store.Store
import com.shabinder.common.preference.SpotiFlyerPreference
internal interface SpotiFlyerPreferenceStore : Store<SpotiFlyerPreferenceStore.Intent, SpotiFlyerPreference.State, Nothing> {
sealed class Intent {
data class OpenPlatform(val platformID: String, val platformLink: String) : Intent()
data class ToggleAnalytics(val enabled: Boolean) : Intent()
object GiveDonation : Intent()
object ShareApp : Intent()
}
}

View File

@ -0,0 +1,73 @@
/*
* * Copyright (c) 2021 Shabinder Singh
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.shabinder.common.preference.store
import com.arkivanov.mvikotlin.core.store.Reducer
import com.arkivanov.mvikotlin.core.store.SimpleBootstrapper
import com.arkivanov.mvikotlin.core.store.Store
import com.arkivanov.mvikotlin.core.store.StoreFactory
import com.arkivanov.mvikotlin.extensions.coroutines.SuspendExecutor
import com.shabinder.common.di.preference.PreferenceManager
import com.shabinder.common.models.methods
import com.shabinder.common.preference.SpotiFlyerPreference.State
import com.shabinder.common.preference.store.SpotiFlyerPreferenceStore.Intent
internal class SpotiFlyerPreferenceStoreProvider(
private val storeFactory: StoreFactory,
private val preferenceManager: PreferenceManager
) {
fun provide(): SpotiFlyerPreferenceStore =
object :
SpotiFlyerPreferenceStore,
Store<Intent, State, Nothing> by storeFactory.create(
name = "SpotiFlyerPreferenceStore",
initialState = State(),
bootstrapper = SimpleBootstrapper(Unit),
executorFactory = ::ExecutorImpl,
reducer = ReducerImpl
) {}
private sealed class Result {
data class ToggleAnalytics(val isEnabled: Boolean) : Result()
}
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
override suspend fun executeAction(action: Unit, getState: () -> State) {
dispatch(Result.ToggleAnalytics(preferenceManager.isAnalyticsEnabled))
}
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
when (intent) {
is Intent.OpenPlatform -> methods.value.openPlatform(intent.platformID, intent.platformLink)
is Intent.GiveDonation -> methods.value.giveDonation()
is Intent.ShareApp -> methods.value.shareApp()
is Intent.ToggleAnalytics -> {
dispatch(Result.ToggleAnalytics(intent.enabled))
preferenceManager.toggleAnalytics(intent.enabled)
}
}
}
}
private object ReducerImpl : Reducer<State, Result> {
override fun State.reduce(result: Result): State =
when (result) {
is Result.ToggleAnalytics -> copy(isAnalyticsEnabled = result.isEnabled)
}
}
}