mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-12-22 20:57:54 +01:00
Preference Screen (WIP)
This commit is contained in:
parent
c3c71813c2
commit
08aa6d0f18
@ -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!")
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
)
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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<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)
|
||||
}
|
@ -62,7 +62,7 @@ interface SpotiFlyerList {
|
||||
/*
|
||||
* Snooze Donation Dialog
|
||||
* */
|
||||
fun snoozeDonationDialog()
|
||||
fun dismissDonationDialogSetOffset()
|
||||
|
||||
interface Dependencies {
|
||||
val storeFactory: StoreFactory
|
||||
|
@ -78,7 +78,7 @@ internal class SpotiFlyerListImpl(
|
||||
store.accept(Intent.RefreshTracksStatuses)
|
||||
}
|
||||
|
||||
override fun snoozeDonationDialog() {
|
||||
override fun dismissDonationDialogSetOffset() {
|
||||
preferenceManager.setDonationOffset(offset = 10)
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,8 @@ interface SpotiFlyerMain {
|
||||
* */
|
||||
suspend fun loadImage(url: String): Picture
|
||||
|
||||
fun dismissDonationDialogOffset()
|
||||
|
||||
interface Dependencies {
|
||||
val mainOutput: Consumer<Output>
|
||||
val storeFactory: StoreFactory
|
||||
|
@ -82,4 +82,8 @@ internal class SpotiFlyerMainImpl(
|
||||
dir.loadImage(url, 150, 150)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dismissDonationDialogOffset() {
|
||||
preferenceManager.setDonationOffset()
|
||||
}
|
||||
}
|
||||
|
35
common/preference/build.gradle.kts
Normal file
35
common/preference/build.gradle.kts
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
common/preference/src/androidMain/AndroidManifest.xml
Normal file
18
common/preference/src/androidMain/AndroidManifest.xml
Normal 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"/>
|
@ -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)
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user