mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 09:04:32 +01:00
Preference Screen (WIP)
This commit is contained in:
parent
c3c71813c2
commit
08aa6d0f18
@ -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!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -82,4 +82,8 @@ internal class SpotiFlyerMainImpl(
|
|||||||
dir.loadImage(url, 150, 150)
|
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