mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-14 16:27:06 +05:30
feat(store): set up a preliminary implementation for newest posts
This commit is contained in:
parent
14007c6e8f
commit
45701f414b
11 changed files with 165 additions and 12 deletions
|
@ -15,6 +15,7 @@ import com.squareup.anvil.annotations.ContributesTo
|
|||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dev.msfjarvis.claw.database.LobstersDatabase
|
||||
import dev.msfjarvis.claw.database.local.CachedNewestPost
|
||||
import dev.msfjarvis.claw.database.local.PostComments
|
||||
import dev.msfjarvis.claw.database.local.SavedPost
|
||||
import dev.msfjarvis.claw.database.model.CSVAdapter
|
||||
|
@ -37,8 +38,10 @@ object DatabaseModule {
|
|||
)
|
||||
return LobstersDatabase(
|
||||
driver = driver,
|
||||
PostCommentsAdapter = PostComments.Adapter(CSVAdapter()),
|
||||
SavedPostAdapter = SavedPost.Adapter(IntColumnAdapter, CSVAdapter()),
|
||||
PostCommentsAdapter = PostComments.Adapter(CSVAdapter),
|
||||
SavedPostAdapter = SavedPost.Adapter(IntColumnAdapter, CSVAdapter),
|
||||
CachedNewestPostAdapter =
|
||||
CachedNewestPost.Adapter(IntColumnAdapter, IntColumnAdapter, CSVAdapter),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ package dev.msfjarvis.claw.database.model
|
|||
|
||||
import app.cash.sqldelight.ColumnAdapter
|
||||
|
||||
class CSVAdapter : ColumnAdapter<List<String>, String> {
|
||||
object CSVAdapter : ColumnAdapter<List<String>, String> {
|
||||
private const val SEPARATOR = ","
|
||||
|
||||
override fun decode(databaseValue: String): List<String> {
|
||||
return databaseValue.split(SEPARATOR)
|
||||
}
|
||||
|
@ -16,8 +18,4 @@ class CSVAdapter : ColumnAdapter<List<String>, String> {
|
|||
override fun encode(value: List<String>): String {
|
||||
return value.joinToString(SEPARATOR)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val SEPARATOR = ","
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import kotlin.Int;
|
||||
import kotlin.String;
|
||||
import kotlin.collections.List;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS CachedNewestPost(
|
||||
pageNumber INTEGER AS Int,
|
||||
shortId TEXT NOT NULL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
commentCount INTEGER AS Int,
|
||||
commentsUrl TEXT NOT NULL,
|
||||
tags TEXT AS List<String> NOT NULL
|
||||
);
|
||||
|
||||
insertPost:
|
||||
INSERT OR REPLACE
|
||||
INTO CachedNewestPost
|
||||
VALUES ?;
|
||||
|
||||
getPage:
|
||||
SELECT *
|
||||
FROM CachedNewestPost
|
||||
WHERE pageNumber = ?;
|
||||
|
||||
clearPage:
|
||||
DELETE FROM CachedNewestPost
|
||||
WHERE pageNumber = ?;
|
||||
|
||||
deleteAll:
|
||||
DELETE FROM CachedNewestPost;
|
14
database/src/main/sqldelight/migrations/5.sqm
Normal file
14
database/src/main/sqldelight/migrations/5.sqm
Normal file
|
@ -0,0 +1,14 @@
|
|||
import kotlin.Int;
|
||||
import kotlin.String;
|
||||
import kotlin.collections.List;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS CachedNewestPost(
|
||||
pageNumber INTEGER AS Int,
|
||||
shortId TEXT NOT NULL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
commentCount INTEGER AS Int,
|
||||
commentsUrl TEXT NOT NULL,
|
||||
tags TEXT AS List<String> NOT NULL
|
||||
);
|
|
@ -25,8 +25,9 @@ class PostCommentsQueriesTest {
|
|||
val database =
|
||||
LobstersDatabase(
|
||||
driver,
|
||||
PostComments.Adapter(CSVAdapter()),
|
||||
SavedPost.Adapter(IntColumnAdapter, CSVAdapter()),
|
||||
CachedNewestPost.Adapter(IntColumnAdapter, IntColumnAdapter, CSVAdapter),
|
||||
PostComments.Adapter(CSVAdapter),
|
||||
SavedPost.Adapter(IntColumnAdapter, CSVAdapter),
|
||||
)
|
||||
postQueries = database.postCommentsQueries
|
||||
}
|
||||
|
|
|
@ -24,8 +24,9 @@ class SavedPostQueriesTest {
|
|||
val database =
|
||||
LobstersDatabase(
|
||||
driver,
|
||||
PostComments.Adapter(CSVAdapter()),
|
||||
SavedPost.Adapter(IntColumnAdapter, CSVAdapter()),
|
||||
CachedNewestPost.Adapter(IntColumnAdapter, IntColumnAdapter, CSVAdapter),
|
||||
PostComments.Adapter(CSVAdapter),
|
||||
SavedPost.Adapter(IntColumnAdapter, CSVAdapter),
|
||||
)
|
||||
postQueries = database.savedPostQueries
|
||||
}
|
||||
|
|
|
@ -19,8 +19,13 @@ anvil { generateDaggerFactories.set(true) }
|
|||
dependencies {
|
||||
api(projects.database)
|
||||
api(projects.model)
|
||||
api(projects.core)
|
||||
|
||||
implementation(projects.api)
|
||||
implementation(libs.dagger)
|
||||
implementation(libs.javax.inject)
|
||||
implementation(libs.kotlinx.atomicfu)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.sqldelight.extensions.coroutines)
|
||||
implementation(libs.store5)
|
||||
}
|
||||
|
|
4
store/lint-baseline.xml
Normal file
4
store/lint-baseline.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<issues format="6" by="lint 8.2.0-alpha15" type="baseline" client="gradle" dependencies="true" name="AGP (8.2.0-alpha15)" variant="all" version="8.2.0-alpha15">
|
||||
|
||||
</issues>
|
|
@ -1,4 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!--
|
||||
~ Copyright © 2021-2023 Harsh Shandilya.
|
||||
~ Use of this source code is governed by an MIT-style
|
||||
~ license that can be found in the LICENSE file or at
|
||||
~ https://opensource.org/licenses/MIT.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.store
|
||||
|
||||
import app.cash.sqldelight.coroutines.asFlow
|
||||
import app.cash.sqldelight.coroutines.mapToList
|
||||
import com.slack.eithernet.ApiResult
|
||||
import dev.msfjarvis.claw.api.LobstersApi
|
||||
import dev.msfjarvis.claw.core.injection.DatabaseDispatcher
|
||||
import dev.msfjarvis.claw.core.injection.IODispatcher
|
||||
import dev.msfjarvis.claw.database.LobstersDatabase
|
||||
import dev.msfjarvis.claw.database.local.CachedNewestPost
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
import dev.msfjarvis.claw.store.utils.toCachedNewest
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.mobilenativefoundation.store.store5.Fetcher
|
||||
import org.mobilenativefoundation.store.store5.SourceOfTruth
|
||||
import org.mobilenativefoundation.store.store5.Store
|
||||
import org.mobilenativefoundation.store.store5.StoreBuilder
|
||||
|
||||
class NewestPostsStore
|
||||
@Inject
|
||||
constructor(
|
||||
private val api: LobstersApi,
|
||||
private val database: LobstersDatabase,
|
||||
@IODispatcher private val ioDispatcher: CoroutineDispatcher,
|
||||
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher,
|
||||
) :
|
||||
Store<Int, List<CachedNewestPost>> by StoreBuilder.from<
|
||||
Int, List<LobstersPost>, List<CachedNewestPost>
|
||||
>(
|
||||
fetcher =
|
||||
Fetcher.of { key ->
|
||||
withContext(ioDispatcher) {
|
||||
when (val result = api.getNewestPosts(key)) {
|
||||
is ApiResult.Success -> result.value
|
||||
is ApiResult.Failure.NetworkFailure -> throw result.error
|
||||
is ApiResult.Failure.UnknownFailure -> throw result.error
|
||||
is ApiResult.Failure.HttpFailure,
|
||||
is ApiResult.Failure.ApiFailure ->
|
||||
throw IOException("API returned an invalid response")
|
||||
}
|
||||
}
|
||||
},
|
||||
sourceOfTruth =
|
||||
SourceOfTruth.of(
|
||||
reader = { page ->
|
||||
database.cachedNewestPostQueries.getPage(page).asFlow().mapToList(dbDispatcher)
|
||||
},
|
||||
writer = { page, items ->
|
||||
database.transaction {
|
||||
items
|
||||
.map { it.toCachedNewest(page) }
|
||||
.forEach { database.cachedNewestPostQueries.insertPost(it) }
|
||||
}
|
||||
},
|
||||
delete = { page -> database.cachedNewestPostQueries.clearPage(page) },
|
||||
deleteAll = { database.cachedNewestPostQueries.deleteAll() },
|
||||
),
|
||||
)
|
||||
.build()
|
23
store/src/main/kotlin/dev/msfjarvis/claw/store/utils/ext.kt
Normal file
23
store/src/main/kotlin/dev/msfjarvis/claw/store/utils/ext.kt
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright © 2023 Harsh Shandilya.
|
||||
* Use of this source code is governed by an MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
package dev.msfjarvis.claw.store.utils
|
||||
|
||||
import dev.msfjarvis.claw.database.local.CachedNewestPost
|
||||
import dev.msfjarvis.claw.model.LobstersPost
|
||||
|
||||
internal fun LobstersPost.toCachedNewest(page: Int): CachedNewestPost {
|
||||
return CachedNewestPost(
|
||||
pageNumber = page,
|
||||
shortId = shortId,
|
||||
title = title,
|
||||
url = url,
|
||||
description = description,
|
||||
commentCount = commentCount,
|
||||
commentsUrl = commentsUrl,
|
||||
tags = tags,
|
||||
)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue