Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Access the latest APK for Kotlin Dictionary from the link below.
- [ ] Add a `Contributors Page` to showcase project contributors
- [ ] Add a `Settings Page` with basic preferences
- [ ] Implement a `Splash Screen`
- [x] Integrate multiplatform paging for `Topic Screen`

---

Expand Down
2 changes: 2 additions & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ kotlin {
implementation(libs.arrow.core)
implementation(libs.arrow.fx.coroutines)
implementation(project(":design-system"))
implementation(libs.cashapp.paging.common)
implementation(libs.cashapp.paging.compose.common)
}
desktopMain.dependencies {
implementation(compose.desktop.currentOs)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.developersbreach.kotlindictionarymultiplatform.previews

import app.cash.paging.PagingData
import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.CodeExample
import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.KotlinTopicDetails
import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.Section
import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.Syntax
import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.ItemTopic
import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.TopicResponse
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.Topic
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf

internal fun sampleCodeSnippet(): String {
return """
Expand Down Expand Up @@ -51,37 +54,41 @@ internal fun fakeTopicDetails(): KotlinTopicDetails {
)
}

private fun sampleTopicList(): List<Topic> {
private fun sampleTopicList(): List<TopicResponse> {
return listOf(
Topic(
TopicResponse(
name = "Smart Casts",
description = "Automatic casting by the compiler after type checks.",
),
Topic(
TopicResponse(
name = "Null Safety",
description = "Kotlin's system to eliminate null pointer exceptions at compile time.",
),
Topic(
TopicResponse(
name = "Coroutines",
description = "Lightweight threads for asynchronous and non-blocking programming.",
),
Topic(
TopicResponse(
name = "Lambdas",
description = "Anonymous functions used to pass behavior as data.",
),
Topic(
TopicResponse(
name = "Sealed Classes",
description = "Classes used to represent restricted class hierarchies for type safety.",
),
)
}

internal fun sampleTopicUiList(): List<ItemTopic> {
internal fun sampleTopicUiList(): List<Topic> {
return sampleTopicList().map { topic ->
ItemTopic(
Topic(
name = topic.name ?: "",
initial = topic.name?.firstOrNull()?.uppercase() ?: "",
description = topic.description ?: "",
)
}
}

internal fun samplePagingData(): Flow<PagingData<Topic>> {
return flowOf(PagingData.from(sampleTopicUiList()))
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package com.developersbreach.kotlindictionarymultiplatform.previews.topic

import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.PreviewLightDark
import com.developersbreach.kotlindictionarymultiplatform.previews.sampleTopicUiList
import app.cash.paging.compose.collectAsLazyPagingItems
import com.developersbreach.kotlindictionarymultiplatform.previews.samplePagingData
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.TopicScreenUI
import com.developersbreach.kotlindictionarymultiplatform.ui.theme.KotlinDictionaryTheme

@PreviewLightDark
@Composable
private fun TopicScreenPreview() {
KotlinDictionaryTheme {
val pagingItems = samplePagingData().collectAsLazyPagingItems()
TopicScreenUI(
topics = sampleTopicUiList(),
topics = pagingItems,
searchQuery = "Search",
onQueryChange = { },
onTopicClick = { },
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class TopicResponse(
data class TopicsResponse(
@SerialName("documents") val topics: List<RawTopic>,
)

Expand All @@ -24,8 +24,14 @@ data class RawField(
@SerialName("stringValue") val value: String,
)

fun RawTopic.toTopic(): Topic {
return Topic(
@Serializable
data class TopicResponse(
val name: String?,
val description: String?,
)

fun RawTopic.toTopic(): TopicResponse {
return TopicResponse(
name = fields.name.value,
description = fields.description.value,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.developersbreach.kotlindictionarymultiplatform.data.topic.repository

import app.cash.paging.PagingSource
import app.cash.paging.PagingState
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.Topic

class TopicPagingSource(
private val repository: TopicRepository,
private val query: String,
) : PagingSource<Int, Topic>() {

override suspend fun load(
params: LoadParams<Int>,
): LoadResult<Int, Topic> {
val page = params.key ?: 1
val pageSize = params.loadSize
return try {
val pageItems = repository.getTopicsPage(page, pageSize, query)
LoadResult.Page(
data = pageItems,
prevKey = if (page == 1) null else page - 1,
nextKey = if (pageItems.isEmpty()) null else page + 1,
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}

override fun getRefreshKey(
state: PagingState<Int, Topic>,
): Int? {
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
package com.developersbreach.kotlindictionarymultiplatform.data.topic.repository

import arrow.core.Either
import arrow.core.getOrElse
import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.TopicsResponse
import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.TopicResponse
import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic
import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.toTopic
import com.developersbreach.kotlindictionarymultiplatform.core.network.topicSource.FirestoreConstants
import com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic.Topic
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get

class TopicRepository(
private val httpClient: HttpClient,
) {
suspend fun getTopics(): Either<Throwable, List<Topic>> {
private suspend fun getTopics(): Either<Throwable, List<TopicResponse>> {
return Either.catch {
val topicResponse: TopicResponse = httpClient.get(FirestoreConstants.TOPICS_URL).body()
topicResponse.topics.map { it.toTopic() }
val topicsResponse: TopicsResponse = httpClient.get(FirestoreConstants.TOPICS_URL).body()
topicsResponse.topics.map { it.toTopic() }
}
}

suspend fun getTopicsPage(
page: Int,
pageSize: Int,
query: String,
): List<Topic> {
val allTopics = getTopics().getOrElse { emptyList() }
val filteredTopics = allTopics
.filter { it.name?.contains(query, ignoreCase = true) == true }
.sortedBy { it.name?.lowercase() ?: "" }
.map { topic ->
Topic(
name = topic.name ?: "",
initial = topic.name?.firstOrNull()?.uppercase() ?: "",
description = topic.description ?: "",
)
}
val fromIndex = (page - 1) * pageSize
val toIndex = (fromIndex + pageSize).coerceAtMost(filteredTopics.size)
return if (fromIndex < filteredTopics.size) filteredTopics.subList(fromIndex, toIndex) else emptyList()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic

data class Topic(
val name: String,
val initial: String,
val description: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ import com.developersbreach.designsystem.components.KdText

@Composable
fun TopicCard(
itemTopic: ItemTopic,
topic: String,
description: String,
topic: Topic,
onCardClick: () -> Unit,
) {
KdSurface(
Expand Down Expand Up @@ -60,7 +58,7 @@ fun TopicCard(
) {
KdText(
modifier = Modifier,
text = itemTopic.initial,
text = topic.initial,
)
}

Expand All @@ -71,7 +69,7 @@ fun TopicCard(
) {
KdText(
modifier = Modifier,
text = topic,
text = topic.name,
style = MaterialTheme.typography.headlineMedium.copy(
color = MaterialTheme.colorScheme.onPrimary,
),
Expand All @@ -81,7 +79,7 @@ fun TopicCard(
Spacer(modifier = Modifier.height(6.dp))
KdText(
modifier = Modifier,
text = description,
text = topic.description,
style = MaterialTheme.typography.labelMedium.copy(
color = MaterialTheme.colorScheme.onBackground,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@ package com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.cash.paging.compose.LazyPagingItems

@Composable
fun TopicList(
topics: List<ItemTopic>,
topics: LazyPagingItems<Topic>,
onTopicClick: (String) -> Unit,
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(bottom = 40.dp),
) {
items(topics) { topic ->
TopicCard(
topic = topic.name,
itemTopic = topic,
description = topic.description,
onCardClick = { onTopicClick(topic.name) },
)
items(topics.itemCount) { index ->
val topic = topics[index]
topic?.let {
TopicCard(
topic = it,
onCardClick = { onTopicClick(it.name) },
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,20 @@ package com.developersbreach.kotlindictionarymultiplatform.ui.screens.topic

import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import com.developersbreach.kotlindictionarymultiplatform.ui.components.UiStateHandler
import app.cash.paging.compose.collectAsLazyPagingItems

@Composable
fun TopicScreen(
viewModel: TopicViewModel,
onTopicClick: (String) -> Unit,
) {
val uiState by viewModel.uiState.collectAsState()
val pagingItems = viewModel.topics.collectAsLazyPagingItems()
val searchQuery = viewModel.searchQuery.collectAsState().value

UiStateHandler(
uiState = uiState,
) { data ->
TopicScreenUI(
topics = data.filteredTopics,
searchQuery = data.searchQuery,
onQueryChange = viewModel::updateSearchQuery,
onTopicClick = onTopicClick,
)
}
TopicScreenUI(
topics = pagingItems,
searchQuery = searchQuery,
onQueryChange = viewModel::updateSearchQuery,
onTopicClick = onTopicClick,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.developersbreach.designsystem.components.KdScaffold
import app.cash.paging.compose.LazyPagingItems

@Composable
fun TopicScreenUI(
topics: List<ItemTopic>,
topics: LazyPagingItems<Topic>,
searchQuery: String,
onQueryChange: (String) -> Unit,
onTopicClick: (String) -> Unit,
Expand Down

This file was deleted.

Loading
Loading