Skip to content

Commit a777c77

Browse files
committed
spotless
1 parent ecae908 commit a777c77

9 files changed

Lines changed: 161 additions & 41 deletions

File tree

data/persistence/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ kotlin {
2828
implementation(libs.multiplatform.settings)
2929
implementation(libs.kotlinx.serialization.json)
3030
implementation(libs.io.insert.koin.core)
31+
implementation(libs.store)
3132
}
3233
}
3334

data/repository/build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ kotlin {
3030
implementation(libs.org.jetbrains.kotlin.test.junit)
3131
}
3232
}
33+
val androidMain by getting {
34+
dependencies {
35+
api (libs.org.jetbrains.kotlinx.atomicfu)
36+
}
37+
}
3338
val desktopTest by getting {
3439
dependencies {
3540
implementation(kotlin("test"))

data/repository/src/commonMain/kotlin/social/androiddev/common/repository/timeline/RealHomeTimelineRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class RealHomeTimelineRepository(
2727
* on first return will also call network fetcher to get
2828
* latest from network and update local storage with it]
2929
*/
30-
override suspend fun read(
30+
override fun read(
3131
feedType: FeedType,
3232
refresh: Boolean
3333
): Flow<StoreResponse<List<StatusLocal>>> {

data/repository/src/commonMain/kotlin/social/androiddev/common/repository/timeline/TimelineSourceOfTruth.kt

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,3 @@
1-
/*
2-
* This file is part of Dodo.
3-
*
4-
* Dodo 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.
5-
*
6-
* Dodo 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.
7-
*
8-
* You should have received a copy of the GNU General Public License along with Dodo. If not, see <https://www.gnu.org/licenses/>.
9-
*/
101
package social.androiddev.common.repository.timeline
112

123
import com.squareup.sqldelight.runtime.coroutines.asFlow
@@ -38,13 +29,13 @@ private fun TimelineQueries.homeItemsAsLocal(key: FeedType) = selectHomeItems()
3829
.asFlow()
3930
.mapToList()
4031
.map {
41-
it.ifEmpty { return@map null } // treat empty list as no result otherwise
32+
it.ifEmpty { return@map null } //treat empty list as no result otherwise
4233
it.map { item -> item.toLocal(key) }
4334
}
4435

4536
fun TimelineDatabase.tryWriteItem(it: StatusDB, type: FeedType): Boolean = try {
4637
timelineQueries.insertFeedItem(
47-
it.copy(type = type.type)
38+
it.copy(type = type.type)
4839
)
4940
true
5041
} catch (t: Throwable) {
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package social.androiddev.common.repository.timeline
2+
3+
import kotlinx.coroutines.flow.Flow
4+
import kotlinx.coroutines.flow.first
5+
import kotlinx.coroutines.flow.flow
6+
import kotlinx.coroutines.test.TestResult
7+
import kotlinx.coroutines.test.runTest
8+
import org.mobilenativefoundation.store.store5.ExperimentalStoreApi
9+
import org.mobilenativefoundation.store.store5.ResponseOrigin
10+
import org.mobilenativefoundation.store.store5.Store
11+
import org.mobilenativefoundation.store.store5.StoreRequest
12+
import org.mobilenativefoundation.store.store5.StoreResponse
13+
import social.androiddev.common.repository.timeline.fixtures.failureResponse
14+
import social.androiddev.common.repository.timeline.fixtures.fakeLocalStatus
15+
import social.androiddev.domain.timeline.FeedType
16+
import social.androiddev.domain.timeline.model.StatusLocal
17+
import kotlin.test.Test
18+
import kotlin.test.assertTrue
19+
import kotlin.test.fail
20+
21+
22+
class RealHomeTimelineRepositoryTest{
23+
@Test fun sucessTest(): TestResult {
24+
return runTest{
25+
val testRepo = RealHomeTimelineRepository(fakeSuccessStore)
26+
val result = testRepo.read(FeedType.Home).first()
27+
assertTrue { result is StoreResponse.Data }
28+
assertTrue { result.requireData().first() == fakeLocalStatus }
29+
30+
}
31+
}
32+
33+
@Test fun failureTest(): TestResult {
34+
return runTest{
35+
val testRepo = RealHomeTimelineRepository(fakeFailureStore)
36+
val result = testRepo.read(FeedType.Home).first()
37+
assertTrue { result is StoreResponse.Error.Message }
38+
assertTrue { result.errorMessageOrNull() == failureResponse.message}
39+
40+
}
41+
}
42+
}
43+
val fakeSuccessStore = object : Store<FeedType, List<StatusLocal>> {
44+
override suspend fun clear(key: FeedType) {
45+
TODO("Not yet implemented")
46+
}
47+
48+
@ExperimentalStoreApi
49+
override suspend fun clearAll() {
50+
TODO("Not yet implemented")
51+
}
52+
53+
override fun stream(request: StoreRequest<FeedType>): Flow<StoreResponse<List<StatusLocal>>> {
54+
return when (request.key.type) {
55+
FeedType.Home.type -> {
56+
flow {
57+
emit(
58+
StoreResponse.Data(
59+
listOf(fakeLocalStatus),
60+
ResponseOrigin.Cache
61+
)
62+
)
63+
}
64+
}
65+
66+
else -> {
67+
fail("wrong response")
68+
}
69+
}
70+
}
71+
}
72+
73+
val fakeFailureStore = object : Store<FeedType, List<StatusLocal>> {
74+
override suspend fun clear(key: FeedType) {
75+
TODO("Not yet implemented")
76+
}
77+
78+
@ExperimentalStoreApi
79+
override suspend fun clearAll() {
80+
TODO("Not yet implemented")
81+
}
82+
83+
override fun stream(request: StoreRequest<FeedType>): Flow<StoreResponse<List<StatusLocal>>> {
84+
return when (request.key.type) {
85+
FeedType.Home.type -> {
86+
flow {
87+
emit(failureResponse)
88+
}
89+
}
90+
91+
else -> {
92+
fail("wrong response")
93+
}
94+
}
95+
}
96+
}

data/repository/src/commonTest/kotlin/social/androiddev/common/repository/timeline/fixtures/TestFixtures.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
*/
1010
package social.androiddev.common.repository.timeline.fixtures
1111

12+
import org.mobilenativefoundation.store.store5.ResponseOrigin
13+
import org.mobilenativefoundation.store.store5.StoreResponse
1214
import social.androiddev.common.network.MastodonApi
1315
import social.androiddev.common.network.model.Application
1416
import social.androiddev.common.network.model.AvailableInstance
@@ -18,6 +20,8 @@ import social.androiddev.common.network.model.Privacy
1820
import social.androiddev.common.network.model.Status
1921
import social.androiddev.common.network.model.Token
2022
import social.androiddev.common.persistence.localstorage.DodoAuthStorage
23+
import social.androiddev.domain.timeline.FeedType
24+
import social.androiddev.domain.timeline.model.StatusLocal
2125

2226
val fakeStorage = object : DodoAuthStorage {
2327
override var currentDomain: String? = "androiddev.social"
@@ -29,6 +33,26 @@ val fakeStorage = object : DodoAuthStorage {
2933
override fun getAccessToken(server: String): String = "FakeToken"
3034
}
3135

36+
val failureResponse = StoreResponse.Error.Message("We failed", ResponseOrigin.Cache)
37+
38+
39+
val fakeLocalStatus = StatusLocal(
40+
"",
41+
FeedType.Home,
42+
"",
43+
0,
44+
0,
45+
0,
46+
"",
47+
null,
48+
false,
49+
"",
50+
"",
51+
"",
52+
"",
53+
""
54+
)
55+
3256
val fakeStatus = Status(
3357
"",
3458
"",

domain/timeline/src/commonMain/kotlin/social/androiddev/domain/timeline/HomeTimelineRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import org.mobilenativefoundation.store.store5.StoreResponse
1414
import social.androiddev.domain.timeline.model.StatusLocal
1515

1616
interface HomeTimelineRepository {
17-
suspend fun read(
17+
fun read(
1818
feedType: FeedType,
1919
refresh: Boolean = false
2020
): Flow<StoreResponse<List<StatusLocal>>>

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ io-insert-koin = "3.2.0"
2323
com-russhwolf = "1.0.0-RC"
2424
kotlinx-serialization = "1.4.0"
2525
store = "5.0.0-SNAPSHOT"
26+
atomic-fu="0.18.5"
2627
io-github-aakira = "2.6.1"
2728

2829
[libraries]
@@ -54,6 +55,7 @@ kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-c
5455
kotlinx-coroutines-javafx = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-javafx", version.ref = "org-jetbrains-kotlinx-coroutines" }
5556
org-jetbrains-kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "org-jetbrains-kotlinx-coroutines" }
5657
org-jetbrains-kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "org-jetbrains-kotlinx-serialization" }
58+
org-jetbrains-kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version.ref = "atomic-fu" }
5759

5860
org-jetbrains-kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "org-jetbrains-kotlin" }
5961
org-jetbrains-kotlin-test-common = { module = "org.jetbrains.kotlin:kotlin-test-common", version.ref = "org-jetbrains-kotlin" }

ui/timeline/src/commonMain/kotlin/social/androiddev/timeline/navigation/TimelineViewModel.kt

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@ import kotlinx.coroutines.CoroutineScope
1414
import kotlinx.coroutines.SupervisorJob
1515
import kotlinx.coroutines.cancel
1616
import kotlinx.coroutines.flow.MutableStateFlow
17+
import kotlinx.coroutines.flow.SharingStarted
1718
import kotlinx.coroutines.flow.StateFlow
18-
import kotlinx.coroutines.flow.asStateFlow
19-
import kotlinx.coroutines.launch
19+
import kotlinx.coroutines.flow.map
20+
import kotlinx.coroutines.flow.mapLatest
21+
import kotlinx.coroutines.flow.stateIn
2022
import org.mobilenativefoundation.store.store5.ResponseOrigin
2123
import org.mobilenativefoundation.store.store5.StoreResponse
2224
import social.androiddev.domain.timeline.FeedType
@@ -33,35 +35,34 @@ class TimelineViewModel(
3335
private val scope = CoroutineScope(mainContext + SupervisorJob())
3436
private val _state =
3537
MutableStateFlow<StoreResponse<List<FeedItemState>>>(StoreResponse.Loading(ResponseOrigin.SourceOfTruth))
36-
val state: StateFlow<StoreResponse<List<FeedItemState>>> = _state.asStateFlow()
38+
val state: StateFlow<StoreResponse<List<FeedItemState>>> = homeTimelineRepository
39+
.read(FeedType.Home, refresh = true)
40+
.mapLatest(::render)
41+
.stateIn(scope, SharingStarted.Eagerly, StoreResponse.Loading(ResponseOrigin.Cache))
3742

38-
init {
39-
scope.launch {
40-
homeTimelineRepository.read(refresh = true).collect {
41-
when (val response: StoreResponse<List<StatusLocal>> = it) {
42-
is StoreResponse.Data -> {
43-
val result = StoreResponse.Data(
44-
response.value.map {
45-
FeedItemState(
46-
id = it.remoteId,
47-
userAvatarUrl = it.avatarUrl,
48-
date = it.createdAt,
49-
username = it.userName,
50-
acctAddress = it.accountAddress,
51-
message = it.content,
52-
images = emptyList(),
53-
videoUrl = null,
54-
)
55-
},
56-
response.origin
43+
private fun render(it: StoreResponse<List<StatusLocal>>): StoreResponse.Data<List<FeedItemState>> {
44+
return when (val response: StoreResponse<List<StatusLocal>> = it) {
45+
is StoreResponse.Data -> {
46+
val result = StoreResponse.Data(
47+
response.value.map {
48+
FeedItemState(
49+
id = it.remoteId,
50+
userAvatarUrl = it.avatarUrl,
51+
date = it.createdAt,
52+
username = it.userName,
53+
acctAddress = it.accountAddress,
54+
message = it.content,
55+
images = emptyList(),
56+
videoUrl = null,
5757
)
58-
_state.value = result
59-
}
58+
},
59+
response.origin
60+
)
61+
result
62+
}
6063

61-
else -> {
62-
// TODO display error/loading
63-
}
64-
}
64+
else -> {
65+
StoreResponse.Data(emptyList(), it.origin)
6566
}
6667
}
6768
}

0 commit comments

Comments
 (0)