diff --git a/common/compose/src/androidMain/kotlin/com/shabinder/common/uikit/DonationDialog.kt b/common/compose/src/androidMain/kotlin/com/shabinder/common/uikit/DonationDialog.kt index 71eeb0a2..5ad20017 100644 --- a/common/compose/src/androidMain/kotlin/com/shabinder/common/uikit/DonationDialog.kt +++ b/common/compose/src/androidMain/kotlin/com/shabinder/common/uikit/DonationDialog.kt @@ -126,10 +126,10 @@ actual fun DonationDialog( horizontalArrangement = Arrangement.SpaceEvenly, modifier = Modifier.padding(horizontal = 4.dp).fillMaxWidth() ) { - OutlinedButton(onClick = onSnooze) { + OutlinedButton(onClick = onDismiss) { Text("Dismiss.") } - TextButton(onClick = onDismiss, colors = ButtonDefaults.buttonColors()) { + TextButton(onClick = onSnooze, colors = ButtonDefaults.buttonColors()) { Text("Remind Later!") } } diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Color.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Color.kt index 0679396c..edef2cb4 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Color.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/Color.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.graphics.Color val colorPrimary = Color(0xFFFC5C7D) val colorPrimaryDark = Color(0xFFCE1CFF) val colorAccent = Color(0xFF9AB3FF) +val colorAccentVariant = Color(0xFF3457D5) val colorRedError = Color(0xFFFF9494) val colorSuccessGreen = Color(0xFF59C351) val darkBackgroundColor = Color(0xFF000000) diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt index 72dce041..8df74b56 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt @@ -40,9 +40,6 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect 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.Modifier 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.TrackDetails import com.shabinder.common.models.methods +import com.shabinder.common.uikit.dialogs.DonationDialogComponent @OptIn(ExperimentalMaterialApi::class) @Composable @@ -104,25 +102,19 @@ fun SpotiFlyerListContent( state = listState, modifier = Modifier.fillMaxSize(), ) + // Donation Dialog Visibility - var visibilty by remember { mutableStateOf(false) } - DonationDialog( - isVisible = visibilty, - onDismiss = { - visibilty = false - }, - onSnooze = { - visibilty = false - component.snoozeDonationDialog() - } - ) + val (openDonationDialog,dismissDonationDialog,snoozeDonationDialog) = DonationDialogComponent { + component.dismissDonationDialogSetOffset() + } + DownloadAllButton( onClick = { component.onDownloadAllClicked(model.trackList) // Check If we are allowed to show donation Dialog if (model.askForDonation) { // Show Donation Dialog - visibilty = true + openDonationDialog() } }, modifier = Modifier.padding(bottom = 24.dp).align(Alignment.BottomCenter) diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt index d8279536..8fe52a33 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerMainUi.kt @@ -17,21 +17,54 @@ package com.shabinder.common.uikit import androidx.compose.animation.Crossfade -import androidx.compose.foundation.* -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.BorderStroke +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.items import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape 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.Text +import androidx.compose.material.TextField import androidx.compose.material.TextFieldDefaults.textFieldColors import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.Info -import androidx.compose.material.icons.rounded.* -import androidx.compose.runtime.* +import androidx.compose.material.icons.rounded.CardGiftcard +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.Modifier 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.models.DownloadRecord import com.shabinder.common.models.methods +import com.shabinder.common.uikit.dialogs.DonationDialogComponent @Composable fun SpotiFlyerMainContent(component: SpotiFlyerMain) { val model by component.model.subscribeAsState() + val (openDonationDialog,_,_) = DonationDialogComponent { + component.dismissDonationDialogOffset() + } + Column { SearchPanel( model.link, @@ -65,14 +103,17 @@ fun SpotiFlyerMainContent(component: SpotiFlyerMain) { HomeTabBar( model.selectedCategory, HomeCategory.values(), - component::selectCategory + component::selectCategory, ) when (model.selectedCategory) { HomeCategory.About -> AboutColumn( analyticsEnabled = model.isAnalyticsEnabled, - donationDialogOpenEvent = { component.analytics.donationDialogVisit() }, - toggleAnalytics = component::toggleAnalytics + toggleAnalytics = component::toggleAnalytics, + openDonationDialog = { + component.analytics.donationDialogVisit() + openDonationDialog() + } ) HomeCategory.History -> HistoryColumn( model.records.sortedByDescending { it.id }, @@ -98,6 +139,7 @@ fun HomeTabBar( } TabRow( + backgroundColor = transparent, selectedTabIndex = selectedIndex, indicator = indicator, modifier = modifier, @@ -195,7 +237,7 @@ fun SearchPanel( fun AboutColumn( modifier: Modifier = Modifier, analyticsEnabled:Boolean, - donationDialogOpenEvent: () -> Unit, + openDonationDialog: () -> 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( modifier = modifier.fillMaxWidth().padding(vertical = 6.dp) - .clickable( - onClick = { - isDonationDialogVisible = true - donationDialogOpenEvent() - } - ), + .clickable(onClick = openDonationDialog), verticalAlignment = Alignment.CenterVertically ) { Icon(Icons.Rounded.CardGiftcard, "Support Developer", Modifier.size(32.dp)) @@ -504,7 +529,7 @@ fun HomeCategoryTabIndicator( ) { Spacer( modifier.padding(horizontal = 24.dp) - .height(4.dp) + .height(3.dp) .background(color, RoundedCornerShape(topStartPercent = 100, topEndPercent = 100)) ) } diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt index 3c0242a9..2e82a6de 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerRootUi.kt @@ -56,7 +56,7 @@ 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.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.Child import com.shabinder.common.uikit.splash.Splash @@ -125,7 +125,7 @@ fun MainScreen(modifier: Modifier = Modifier, alpha: Float, topPadding: Dp = 0.d ).then(modifier) ) { - val activeComponent = component.routerState.asState() + val activeComponent = component.routerState.subscribeAsState() val callBacks = component.callBacks AppBar( backgroundColor = appBarColor, diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/dialogs/Donation.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/dialogs/Donation.kt index dc98addb..7b7f3db7 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/dialogs/Donation.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/dialogs/Donation.kt @@ -1 +1,33 @@ 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 +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) +} \ No newline at end of file diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerList.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerList.kt index d2feb248..037eb808 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerList.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerList.kt @@ -62,7 +62,7 @@ interface SpotiFlyerList { /* * Snooze Donation Dialog * */ - fun snoozeDonationDialog() + fun dismissDonationDialogSetOffset() interface Dependencies { val storeFactory: StoreFactory diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt index 0b4a38d7..6b167dcf 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt @@ -78,7 +78,7 @@ internal class SpotiFlyerListImpl( store.accept(Intent.RefreshTracksStatuses) } - override fun snoozeDonationDialog() { + override fun dismissDonationDialogSetOffset() { preferenceManager.setDonationOffset(offset = 10) } diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt index 9bfc18ec..d6aa3941 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMain.kt @@ -59,6 +59,8 @@ interface SpotiFlyerMain { * */ suspend fun loadImage(url: String): Picture + fun dismissDonationDialogOffset() + interface Dependencies { val mainOutput: Consumer val storeFactory: StoreFactory diff --git a/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt b/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt index 5872d0c8..fc5ae0b0 100644 --- a/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt +++ b/common/main/src/commonMain/kotlin/com/shabinder/common/main/integration/SpotiFlyerMainImpl.kt @@ -82,4 +82,8 @@ internal class SpotiFlyerMainImpl( dir.loadImage(url, 150, 150) } } + + override fun dismissDonationDialogOffset() { + preferenceManager.setDonationOffset() + } } diff --git a/common/preference/build.gradle.kts b/common/preference/build.gradle.kts new file mode 100644 index 00000000..329a5b62 --- /dev/null +++ b/common/preference/build.gradle.kts @@ -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 . + */ + +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) + } + } + } +} diff --git a/common/preference/src/androidMain/AndroidManifest.xml b/common/preference/src/androidMain/AndroidManifest.xml new file mode 100644 index 00000000..ad1a1031 --- /dev/null +++ b/common/preference/src/androidMain/AndroidManifest.xml @@ -0,0 +1,18 @@ + + + + diff --git a/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/SpotiFlyerPreference.kt b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/SpotiFlyerPreference.kt new file mode 100644 index 00000000..006e204d --- /dev/null +++ b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/SpotiFlyerPreference.kt @@ -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 . + */ + +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 + + val analytics: Analytics + + fun toggleAnalytics(enabled: Boolean) + + fun setDownloadDirectory(newBasePath: String) + + suspend fun loadImage(url: String): Picture + + interface Dependencies { + val prefOutput: Consumer + 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) diff --git a/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/integration/SpotiFlyerPreferenceImpl.kt b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/integration/SpotiFlyerPreferenceImpl.kt new file mode 100644 index 00000000..18b457d8 --- /dev/null +++ b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/integration/SpotiFlyerPreferenceImpl.kt @@ -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 . + */ + +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() + + override val model: Value = 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) + } + } +} diff --git a/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/InstanceKeeperExt.kt b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/InstanceKeeperExt.kt new file mode 100644 index 00000000..c21de6bf --- /dev/null +++ b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/InstanceKeeperExt.kt @@ -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 . + */ + +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 > InstanceKeeper.getStore(key: Any, factory: () -> T): T = + getOrCreate(key) { StoreHolder(factory()) } + .store + +inline fun > InstanceKeeper.getStore(noinline factory: () -> T): T = + getStore(T::class, factory) + +private class StoreHolder>( + val store: T +) : InstanceKeeper.Instance { + override fun onDestroy() { + store.dispose() + } +} diff --git a/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/SpotiFlyerPreferenceStore.kt b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/SpotiFlyerPreferenceStore.kt new file mode 100644 index 00000000..054fff9d --- /dev/null +++ b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/SpotiFlyerPreferenceStore.kt @@ -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 . + */ + +package com.shabinder.common.preference.store + +import com.arkivanov.mvikotlin.core.store.Store +import com.shabinder.common.preference.SpotiFlyerPreference + +internal interface SpotiFlyerPreferenceStore : Store { + 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() + } +} diff --git a/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/SpotiFlyerPreferenceStoreProvider.kt b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/SpotiFlyerPreferenceStoreProvider.kt new file mode 100644 index 00000000..ba273448 --- /dev/null +++ b/common/preference/src/commonMain/kotlin/com/shabinder/common/preference/store/SpotiFlyerPreferenceStoreProvider.kt @@ -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 . + */ + +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 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() { + 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 { + override fun State.reduce(result: Result): State = + when (result) { + is Result.ToggleAnalytics -> copy(isAnalyticsEnabled = result.isEnabled) + } + } +}