Skip to content

Commit 2fd59cb

Browse files
authored
Refactor repository layer (#30)
* Refactor coins repository * Update SessionPreference.kt * Update localization
1 parent fe47c9d commit 2fd59cb

File tree

16 files changed

+165
-34
lines changed

16 files changed

+165
-34
lines changed

app/src/main/java/com/shifthackz/aisdv1/app/di/PreferenceModule.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package com.shifthackz.aisdv1.app.di
22

33
import android.content.Context
44
import com.shifthackz.aisdv1.data.preference.PreferenceManagerImpl
5+
import com.shifthackz.aisdv1.data.preference.SessionPreferenceImpl
56
import com.shifthackz.aisdv1.domain.preference.PreferenceManager
7+
import com.shifthackz.aisdv1.domain.preference.SessionPreference
68
import org.koin.android.ext.koin.androidContext
9+
import org.koin.core.module.dsl.singleOf
10+
import org.koin.dsl.bind
711
import org.koin.dsl.module
812

913
private const val KEY_PREFERENCE_MANAGER = "aisdv1_preference_manager"
@@ -15,4 +19,6 @@ val preferenceModule = module {
1519
.getSharedPreferences(KEY_PREFERENCE_MANAGER, Context.MODE_PRIVATE)
1620
.let(::PreferenceManagerImpl)
1721
}
22+
23+
singleOf(::SessionPreferenceImpl) bind SessionPreference::class
1824
}

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
buildscript {
22
ext {
3-
appVersion = "0.2.0"
3+
appVersion = "0.2.1"
44
minSdk = 26
55
targetSdk = 33
66
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.shifthackz.aisdv1.core.common.extensions
2+
3+
import java.text.SimpleDateFormat
4+
import java.util.*
5+
6+
fun Date.getRawDay(): Int = SimpleDateFormat("dd", Locale.ROOT).format(this).toInt()
7+
8+
fun Date.getRawMonth(): Int = SimpleDateFormat("MM", Locale.ROOT).format(this).toInt()
9+
10+
fun Date.getRawYear(): Int = SimpleDateFormat("yyyy", Locale.ROOT).format(this).toInt()
11+
12+
fun Date.getDayRange(): Pair<Date, Date> {
13+
val formatter = SimpleDateFormat("dd.MM.yyyy'T'HH:mm:ss.SSS", Locale.ROOT)
14+
val prefix = "${getRawDay()}.${getRawMonth()}.${getRawYear()}"
15+
val start = formatter.parse("${prefix}T00:00:00.000") ?: this
16+
val end = formatter.parse("${prefix}T23:59:59.999") ?: this
17+
return start to end
18+
}

data/src/main/java/com/shifthackz/aisdv1/data/local/CoinLocalDataSource.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ internal class CoinLocalDataSource(
1010
private val coinDao: CoinDao,
1111
) : CoinDataSource.Local {
1212

13-
override fun observeQuerySpentCoinsForPeriod(start: Long, end: Long) = coinDao
14-
.observeQueryAvailableCoinsForPeriod(start, end)
13+
override fun querySpentCoinsForPeriod(start: Long, end: Long) = coinDao
14+
.queryAvailableCoinsForPeriod(start, end)
1515
.map { it.size }
1616

1717
override fun onCoinSpent(): Completable = coinDao.insert(
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.shifthackz.aisdv1.data.preference
2+
3+
import com.shifthackz.aisdv1.domain.preference.SessionPreference
4+
5+
class SessionPreferenceImpl : SessionPreference {
6+
7+
private var _coinsPerDay: Int = -1
8+
9+
override var coinsPerDay: Int
10+
get() = _coinsPerDay
11+
set(value) {
12+
_coinsPerDay = value
13+
}
14+
}

data/src/main/java/com/shifthackz/aisdv1/data/repository/CoinRepositoryImpl.kt

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,65 @@
11
package com.shifthackz.aisdv1.data.repository
22

3+
import com.shifthackz.aisdv1.core.common.extensions.getDayRange
34
import com.shifthackz.aisdv1.domain.datasource.CoinDataSource
45
import com.shifthackz.aisdv1.domain.preference.PreferenceManager
6+
import com.shifthackz.aisdv1.domain.preference.SessionPreference
57
import com.shifthackz.aisdv1.domain.repository.CoinRepository
8+
import io.reactivex.rxjava3.core.BackpressureStrategy
69
import io.reactivex.rxjava3.core.Completable
710
import io.reactivex.rxjava3.core.Flowable
8-
import java.time.LocalDateTime
9-
import java.time.LocalTime
10-
import java.time.ZoneOffset
11+
import io.reactivex.rxjava3.core.Single
12+
import io.reactivex.rxjava3.subjects.PublishSubject
13+
import java.util.*
14+
import java.util.concurrent.TimeUnit
1115

1216
internal class CoinRepositoryImpl(
1317
private val coinLocalDataSource: CoinDataSource.Local,
1418
private val coinRemoteDateSource: CoinDataSource.Remote,
1519
private val preferenceManager: PreferenceManager,
20+
private val sessionPreference: SessionPreference,
1621
) : CoinRepository {
1722

18-
private var availableCoinsPerDay = 10
23+
private val eventCoinSpent: PublishSubject<Unit> = PublishSubject.create()
1924

2025
override fun observeAvailableCoinsForToday(): Flowable<Int> {
21-
val now = LocalDateTime.now()
22-
val start = now.with(LocalTime.MIN).toInstant(ZoneOffset.MIN).toEpochMilli()
23-
val end = now.with(LocalTime.MAX).toInstant(ZoneOffset.MIN).toEpochMilli()
24-
return coinLocalDataSource
25-
.observeQuerySpentCoinsForPeriod(start, end)
26-
.flatMap {
27-
coinRemoteDateSource
28-
.fetchCoinsConfig()
29-
.toFlowable()
30-
.map { coinsPerDay ->
31-
availableCoinsPerDay = coinsPerDay
32-
coinsPerDay - it
33-
}
34-
}
35-
.map { coins -> if (coins < 0) 0 else coins }
26+
val coinsPerDayProducer: () -> Single<Int> = {
27+
val availableCoinsPerDay = sessionPreference.coinsPerDay
28+
if (availableCoinsPerDay != -1) Single.just(availableCoinsPerDay)
29+
else coinRemoteDateSource
30+
.fetchCoinsConfig()
31+
.doOnSuccess { sessionPreference.coinsPerDay = it }
32+
}
33+
34+
val coinsCalculationProducer: () -> Flowable<Int> = {
35+
val (start, end) = Date().getDayRange()
36+
coinsPerDayProducer()
37+
.zipWith(
38+
coinLocalDataSource.querySpentCoinsForPeriod(start.time, end.time),
39+
::Pair,
40+
)
41+
.map { (availableToday, spentToday) -> availableToday - spentToday }
42+
.map { coins -> if (coins < 0) 0 else coins }
43+
.toFlowable()
44+
}
45+
46+
val coinSpentEventProducer: () -> Flowable<*> = {
47+
eventCoinSpent.toFlowable(BackpressureStrategy.LATEST)
48+
}
49+
50+
val coreTickProducer: () -> Flowable<*> = {
51+
Flowable.interval(10L, TimeUnit.SECONDS)
52+
}
53+
54+
return Flowable
55+
.merge(coinsCalculationProducer(), coreTickProducer(), coinSpentEventProducer())
56+
.flatMap { coinsCalculationProducer() }
57+
.replay(1)
58+
.refCount(1, TimeUnit.SECONDS)
3659
}
3760

3861
override fun spendCoin(): Completable {
62+
eventCoinSpent.onNext(Unit)
3963
return if (preferenceManager.useSdAiCloud) coinLocalDataSource.onCoinSpent()
4064
else Completable.complete()
4165
}

domain/src/main/java/com/shifthackz/aisdv1/domain/datasource/CoinDataSource.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package com.shifthackz.aisdv1.domain.datasource
22

33
import io.reactivex.rxjava3.core.Completable
4-
import io.reactivex.rxjava3.core.Flowable
54
import io.reactivex.rxjava3.core.Single
65

76
sealed interface CoinDataSource {
87

98
interface Local : CoinDataSource {
10-
fun observeQuerySpentCoinsForPeriod(start: Long, end: Long): Flowable<Int>
9+
fun querySpentCoinsForPeriod(start: Long, end: Long): Single<Int>
1110
fun onCoinSpent(): Completable
1211
}
1312

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.shifthackz.aisdv1.domain.preference
2+
3+
interface SessionPreference {
4+
var coinsPerDay: Int
5+
}

presentation/src/main/java/com/shifthackz/aisdv1/presentation/activity/AiStableDiffusionActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class AiStableDiffusionActivity : ComponentActivity(), ImagePickerFeature, FileS
143143
},
144144
launchUpdateCheck = {
145145
analytics.logEvent(SettingsCheckUpdate)
146-
versionCheckerViewModel.checkForUpdate(true)
146+
versionCheckerViewModel.checkForUpdate(notifyIfSame = true)
147147
},
148148
launchInAppReview = {
149149
analytics.logEvent(SettingsOpenMarket)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.shifthackz.aisdv1.presentation.widget.dialog
2+
3+
import androidx.compose.foundation.shape.RoundedCornerShape
4+
import androidx.compose.material3.AlertDialog
5+
import androidx.compose.material3.AlertDialogDefaults
6+
import androidx.compose.material3.Text
7+
import androidx.compose.material3.TextButton
8+
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.res.stringResource
10+
import androidx.compose.ui.tooling.preview.Preview
11+
import androidx.compose.ui.unit.dp
12+
import androidx.compose.ui.unit.sp
13+
import androidx.compose.ui.window.DialogProperties
14+
import com.shifthackz.aisdv1.core.common.extensions.EmptyLambda
15+
import com.shifthackz.aisdv1.presentation.R
16+
17+
@Composable
18+
fun ForceUpdateDialog(
19+
openMarket: () -> Unit = {},
20+
) {
21+
AlertDialog(
22+
shape = RoundedCornerShape(24.dp),
23+
onDismissRequest = EmptyLambda,
24+
properties = DialogProperties(
25+
dismissOnClickOutside = false,
26+
dismissOnBackPress = false,
27+
),
28+
confirmButton = {
29+
TextButton(onClick = openMarket) {
30+
Text(text = stringResource(id = R.string.action_update))
31+
}
32+
},
33+
title = {
34+
Text(
35+
text = stringResource(id = R.string.update_required_title),
36+
fontSize = 18.sp,
37+
color = AlertDialogDefaults.titleContentColor,
38+
)
39+
},
40+
text = {
41+
Text(
42+
text = stringResource(id = R.string.update_required_sub_title),
43+
fontSize = 14.sp,
44+
color = AlertDialogDefaults.textContentColor,
45+
)
46+
}
47+
)
48+
}
49+
50+
@Composable
51+
@Preview
52+
private fun ForceUpdateDialogPreview() {
53+
ForceUpdateDialog()
54+
}

0 commit comments

Comments
 (0)