mirror of
https://github.com/msfjarvis/compose-lobsters
synced 2025-08-15 01:47:03 +05:30
refactor: improve read posts search performance
This commit is contained in:
parent
89b821ebf1
commit
cbd7f2fca4
6 changed files with 31 additions and 44 deletions
|
@ -57,8 +57,6 @@ import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.shareIn
|
import kotlinx.coroutines.flow.shareIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import kotlinx.coroutines.sync.withLock
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ContributesViewModel
|
@ContributesViewModel
|
||||||
|
@ -84,7 +82,7 @@ constructor(
|
||||||
pagingSourceFactory = { pagingSourceFactory.create(api::getHottestPosts) },
|
pagingSourceFactory = { pagingSourceFactory.create(api::getHottestPosts) },
|
||||||
)
|
)
|
||||||
.flow
|
.flow
|
||||||
.map(::mapUIPost)
|
.map(::mapToUIPost)
|
||||||
.cachedIn(viewModelScope)
|
.cachedIn(viewModelScope)
|
||||||
|
|
||||||
val newestPosts =
|
val newestPosts =
|
||||||
|
@ -94,7 +92,7 @@ constructor(
|
||||||
pagingSourceFactory = { pagingSourceFactory.create(api::getNewestPosts) },
|
pagingSourceFactory = { pagingSourceFactory.create(api::getNewestPosts) },
|
||||||
)
|
)
|
||||||
.flow
|
.flow
|
||||||
.map(::mapUIPost)
|
.map(::mapToUIPost)
|
||||||
.cachedIn(viewModelScope)
|
.cachedIn(viewModelScope)
|
||||||
val searchResults =
|
val searchResults =
|
||||||
Pager(
|
Pager(
|
||||||
|
@ -103,38 +101,33 @@ constructor(
|
||||||
pagingSourceFactory = { searchPagingSourceFactory.create { searchQuery } },
|
pagingSourceFactory = { searchPagingSourceFactory.create { searchQuery } },
|
||||||
)
|
)
|
||||||
.flow
|
.flow
|
||||||
.map(::mapUIPost)
|
.map(::mapToUIPost)
|
||||||
|
|
||||||
val savedPosts
|
val savedPosts =
|
||||||
get() =
|
savedPostsRepository.savedPosts
|
||||||
savedPostsRepository.savedPosts
|
.map { it.map(UIPost.Companion::fromSavedPost) }
|
||||||
.map { it.map(UIPost.Companion::fromSavedPost) }
|
.shareIn(viewModelScope, started = SharingStarted.Lazily, Int.MAX_VALUE)
|
||||||
.shareIn(viewModelScope, started = SharingStarted.Lazily, Int.MAX_VALUE)
|
|
||||||
|
|
||||||
val savedPostsByMonth
|
val savedPostsByMonth = savedPosts.map(::groupSavedPosts)
|
||||||
get() = savedPosts.map(::mapSavedPosts)
|
|
||||||
|
|
||||||
var searchQuery by mutableStateOf("")
|
var searchQuery by mutableStateOf("")
|
||||||
|
|
||||||
private val _savedPostsMutex = Mutex()
|
private var _readPosts = emptyList<String>()
|
||||||
private var _savedPosts = emptyList<String>()
|
private var _savedPosts = emptyList<String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch { savedPosts.collectLatest { _savedPosts = it.map(UIPost::shortId) } }
|
||||||
savedPosts.collectLatest {
|
viewModelScope.launch { readPostsRepository.readPosts.collectLatest { _readPosts = it } }
|
||||||
_savedPostsMutex.withLock { _savedPosts = it.map(UIPost::shortId) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapUIPost(pagingData: PagingData<LobstersPost>): PagingData<UIPost> {
|
private fun mapToUIPost(pagingData: PagingData<LobstersPost>): PagingData<UIPost> {
|
||||||
return pagingData.map { post ->
|
return pagingData.map { post ->
|
||||||
val uiPost = post.toUIPost()
|
val uiPost = post.toUIPost()
|
||||||
uiPost.copy(isSaved = isPostSaved(uiPost), isRead = isPostRead(uiPost))
|
uiPost.copy(isSaved = isPostSaved(uiPost), isRead = isPostRead(uiPost))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mapSavedPosts(items: List<UIPost>): ImmutableMap<String, List<UIPost>> {
|
private fun groupSavedPosts(items: List<UIPost>): ImmutableMap<String, List<UIPost>> {
|
||||||
val sorted =
|
val sorted =
|
||||||
items.sortedWith { post1, post2 ->
|
items.sortedWith { post1, post2 ->
|
||||||
val post1Date = post1.createdAt.toLocalDateTime()
|
val post1Date = post1.createdAt.toLocalDateTime()
|
||||||
|
@ -159,7 +152,9 @@ constructor(
|
||||||
return _savedPosts.contains(post.shortId)
|
return _savedPosts.contains(post.shortId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isPostRead(post: UIPost) = readPostsRepository.isRead(post.shortId)
|
private fun isPostRead(post: UIPost): Boolean {
|
||||||
|
return _readPosts.contains(post.shortId)
|
||||||
|
}
|
||||||
|
|
||||||
fun toggleSave(post: UIPost) {
|
fun toggleSave(post: UIPost) {
|
||||||
viewModelScope.launch(ioDispatcher) {
|
viewModelScope.launch(ioDispatcher) {
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
*/
|
*/
|
||||||
package dev.msfjarvis.claw.android.viewmodel
|
package dev.msfjarvis.claw.android.viewmodel
|
||||||
|
|
||||||
|
import app.cash.sqldelight.coroutines.asFlow
|
||||||
|
import app.cash.sqldelight.coroutines.mapToList
|
||||||
import dev.msfjarvis.claw.core.injection.DatabaseDispatcher
|
import dev.msfjarvis.claw.core.injection.DatabaseDispatcher
|
||||||
import dev.msfjarvis.claw.database.local.ReadPostsQueries
|
import dev.msfjarvis.claw.database.local.ReadPostsQueries
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -18,12 +20,9 @@ constructor(
|
||||||
private val readPostsQueries: ReadPostsQueries,
|
private val readPostsQueries: ReadPostsQueries,
|
||||||
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher,
|
@DatabaseDispatcher private val dbDispatcher: CoroutineDispatcher,
|
||||||
) {
|
) {
|
||||||
|
val readPosts = readPostsQueries.selectAllPosts().asFlow().mapToList(dbDispatcher)
|
||||||
|
|
||||||
suspend fun markRead(postId: String) {
|
suspend fun markRead(postId: String) {
|
||||||
withContext(dbDispatcher) { readPostsQueries.markRead(postId) }
|
withContext(dbDispatcher) { readPostsQueries.markRead(postId) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isRead(postId: String): Boolean {
|
|
||||||
return readPostsQueries.isRead(postId).executeAsOneOrNull() != null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,15 @@ CREATE TABLE ReadPosts(
|
||||||
id TEXT NOT NULL PRIMARY KEY
|
id TEXT NOT NULL PRIMARY KEY
|
||||||
);
|
);
|
||||||
|
|
||||||
|
selectAllPosts:
|
||||||
|
SELECT *
|
||||||
|
FROM ReadPosts;
|
||||||
|
|
||||||
markRead:
|
markRead:
|
||||||
INSERT OR REPLACE
|
INSERT OR IGNORE
|
||||||
INTO ReadPosts(id)
|
INTO ReadPosts(id)
|
||||||
VALUES (?);
|
VALUES (?);
|
||||||
|
|
||||||
markUnread:
|
markUnread:
|
||||||
DELETE FROM ReadPosts
|
DELETE FROM ReadPosts
|
||||||
WHERE id = ?;
|
WHERE id = ?;
|
||||||
|
|
||||||
isRead:
|
|
||||||
SELECT *
|
|
||||||
FROM ReadPosts
|
|
||||||
WHERE id = ?;
|
|
||||||
|
|
|
@ -36,8 +36,3 @@ deletePost:
|
||||||
DELETE
|
DELETE
|
||||||
FROM SavedPost
|
FROM SavedPost
|
||||||
WHERE shortId = ?;
|
WHERE shortId = ?;
|
||||||
|
|
||||||
selectPost:
|
|
||||||
SELECT *
|
|
||||||
FROM SavedPost
|
|
||||||
WHERE shortId = ?;
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2023 Harsh Shandilya.
|
* Copyright © 2023-2024 Harsh Shandilya.
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE file or at
|
* license that can be found in the LICENSE file or at
|
||||||
* https://opensource.org/licenses/MIT.
|
* https://opensource.org/licenses/MIT.
|
||||||
|
@ -23,8 +23,8 @@ class ReadPostsQueriesTest {
|
||||||
fun `mark post as read`() {
|
fun `mark post as read`() {
|
||||||
val id = UUID.randomUUID().toString()
|
val id = UUID.randomUUID().toString()
|
||||||
postQueries.markRead(id)
|
postQueries.markRead(id)
|
||||||
assertThat(postQueries.isRead(id).executeAsOne()).isNotNull()
|
assertThat(postQueries.selectAllPosts().executeAsList()).contains(id)
|
||||||
postQueries.markUnread(id)
|
postQueries.markUnread(id)
|
||||||
assertThat(postQueries.isRead(id).executeAsOneOrNull()).isNull()
|
assertThat(postQueries.selectAllPosts().executeAsList()).doesNotContain(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright © 2021-2023 Harsh Shandilya.
|
* Copyright © 2021-2024 Harsh Shandilya.
|
||||||
* Use of this source code is governed by an MIT-style
|
* Use of this source code is governed by an MIT-style
|
||||||
* license that can be found in the LICENSE file or at
|
* license that can be found in the LICENSE file or at
|
||||||
* https://opensource.org/licenses/MIT.
|
* https://opensource.org/licenses/MIT.
|
||||||
|
@ -32,7 +32,7 @@ class SavedPostQueriesTest {
|
||||||
@Test
|
@Test
|
||||||
fun `update post in database`() {
|
fun `update post in database`() {
|
||||||
// Get 1 post
|
// Get 1 post
|
||||||
val post = createTestData(1)[0]
|
val post = createTestData(1).first()
|
||||||
|
|
||||||
// Insert post into DB
|
// Insert post into DB
|
||||||
postQueries.insertOrReplacePost(post)
|
postQueries.insertOrReplacePost(post)
|
||||||
|
@ -46,15 +46,14 @@ class SavedPostQueriesTest {
|
||||||
assertThat(postsCount).isEqualTo(1)
|
assertThat(postsCount).isEqualTo(1)
|
||||||
|
|
||||||
// Check if post is updated
|
// Check if post is updated
|
||||||
val postFromDb = postQueries.selectPost(post.shortId).executeAsOne()
|
val postFromDb = postQueries.selectAllPosts().executeAsOne()
|
||||||
|
|
||||||
assertThat(postFromDb.submitterName).isEqualTo("Fake name")
|
assertThat(postFromDb.submitterName).isEqualTo("Fake name")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `get post from db`() {
|
fun `get post from db`() {
|
||||||
// Get 1 post
|
// Get 1 post
|
||||||
val post = createTestData(1)[0]
|
val post = createTestData(1).first()
|
||||||
|
|
||||||
// Insert post into DB
|
// Insert post into DB
|
||||||
postQueries.insertOrReplacePost(post)
|
postQueries.insertOrReplacePost(post)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue