From 9a3ab1b6eeac73c4d075a585a1d060bea8b15955 Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Tue, 17 Jun 2025 16:57:04 +0530 Subject: [PATCH 01/10] - Integrate Firestore for fetching topics data. - Add `description` field to `Topic` and `ItemTopic` models. - Update UI to display topic descriptions. - Add Ktor HTTP client and JSON serialization dependencies. - Update preview data with topic descriptions. --- composeApp/build.gradle.kts | 1 + .../previews/PreviewUtils.kt | 26 ++++++++++--- .../data/topic/model/FirestoreResponse.kt | 19 ++++++++++ .../data/topic/model/Topic.kt | 4 ++ .../data/topic/repository/TopicRepository.kt | 37 ++++++++++--------- .../data/topic/utils/FirestoreConstants.kt | 6 +++ .../data/topic/utils/Mappers.kt | 11 ++++++ .../ui/screens/topic/TopicCard.kt | 4 +- .../ui/screens/topic/TopicList.kt | 5 +-- .../ui/screens/topic/TopicUiState.kt | 1 + .../ui/screens/topic/TopicViewModel.kt | 3 +- gradle/libs.versions.toml | 2 + 12 files changed, 89 insertions(+), 30 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt create mode 100644 composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/FirestoreConstants.kt create mode 100644 composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index c1798c3..24bebdb 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -87,6 +87,7 @@ kotlin { implementation(libs.koin.androidx.compose) implementation(libs.generativeai) implementation(compose.uiTooling) + implementation("io.ktor:ktor-client-okhttp:2.3.7") } commonMain.dependencies { implementation(compose.runtime) diff --git a/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt b/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt index 11dac6b..68061b4 100644 --- a/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt +++ b/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt @@ -53,11 +53,26 @@ internal fun fakeTopicDetails(): KotlinTopicDetails { private fun sampleTopicList(): List { return listOf( - Topic("Smart Casts"), - Topic("Null Safety"), - Topic("Coroutines"), - Topic("Lambdas"), - Topic("Sealed Classes"), + Topic( + name = "Smart Casts", + description = "Automatic casting by the compiler after type checks.", + ), + Topic( + name = "Null Safety", + description = "Kotlin's system to eliminate null pointer exceptions at compile time.", + ), + Topic( + name = "Coroutines", + description = "Lightweight threads for asynchronous and non-blocking programming.", + ), + Topic( + name = "Lambdas", + description = "Anonymous functions used to pass behavior as data.", + ), + Topic( + name = "Sealed Classes", + description = "Classes used to represent restricted class hierarchies for type safety.", + ), ) } @@ -67,6 +82,7 @@ internal fun sampleTopicUiList(): List { name = topic.name, initial = topic.name.firstOrNull()?.uppercase() ?: "", isBookmarked = true, + description = topic.description, ) } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt new file mode 100644 index 0000000..09cd176 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt @@ -0,0 +1,19 @@ +package com.developersbreach.kotlindictionarymultiplatform.data.topic.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class FirestoreDocument( + @SerialName("fields") val fields: Map, +) + +@Serializable +data class FirestoreField( + @SerialName("stringValue") val stringValue: String, +) + +@Serializable +data class FirestoreResponse( + @SerialName("documents") val documents: List, +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/Topic.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/Topic.kt index 91f9539..873d1d3 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/Topic.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/Topic.kt @@ -1,5 +1,9 @@ package com.developersbreach.kotlindictionarymultiplatform.data.topic.model +import kotlinx.serialization.Serializable + +@Serializable data class Topic( val name: String, + val description: String, ) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt index 85462f4..eebec49 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt @@ -1,28 +1,29 @@ package com.developersbreach.kotlindictionarymultiplatform.data.topic.repository import arrow.core.Either +import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.FirestoreResponse import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic +import com.developersbreach.kotlindictionarymultiplatform.data.topic.utils.FirestoreConstants +import com.developersbreach.kotlindictionarymultiplatform.data.topic.utils.toTopic +import io.ktor.client.HttpClient +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.serialization.kotlinx.json.json +import kotlinx.serialization.json.Json object TopicRepository { - fun getTopics(): Either> { + + private val client = HttpClient { + install(ContentNegotiation) { + json(Json { ignoreUnknownKeys = true }) + } + } + + suspend fun getTopics(): Either> { return Either.catch { - listOf( - Topic("Variables"), - Topic("Strings"), - Topic("Functions"), - Topic("Coroutines"), - Topic("Classes"), - Topic("Interfaces"), - Topic("Objects"), - Topic("Collections"), - Topic("Null Safety"), - Topic("Lambdas"), - Topic("Higher-Order Functions"), - Topic("Delegation"), - Topic("Sealed Classes"), - Topic("Generics"), - Topic("Annotations"), - ) + val response: FirestoreResponse = client.get(FirestoreConstants.TOPICS_URL).body() + response.documents.map { it.toTopic() } } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/FirestoreConstants.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/FirestoreConstants.kt new file mode 100644 index 0000000..c1e1579 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/FirestoreConstants.kt @@ -0,0 +1,6 @@ +package com.developersbreach.kotlindictionarymultiplatform.data.topic.utils + +object FirestoreConstants { + const val TOPICS_URL = + "https://firestore.googleapis.com/v1/projects/kotlin-dictionary/databases/(default)/documents/topics" +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt new file mode 100644 index 0000000..99f9297 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt @@ -0,0 +1,11 @@ +package com.developersbreach.kotlindictionarymultiplatform.data.topic.utils + +import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.FirestoreDocument +import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic + +fun FirestoreDocument.toTopic(): Topic { + return Topic( + name = fields["name"]?.stringValue.orEmpty(), + description = fields["description"]?.stringValue.orEmpty(), + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt index 28de7c0..2a4d333 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt @@ -35,7 +35,7 @@ import org.jetbrains.compose.resources.stringResource fun TopicCard( itemTopic: ItemTopic, topic: String, - subtitle: String, + description: String, isBookmarked: Boolean, onBookmarkClick: () -> Unit, onCardClick: () -> Unit, @@ -91,7 +91,7 @@ fun TopicCard( Spacer(modifier = Modifier.height(6.dp)) KdText( modifier = Modifier, - text = subtitle, + text = description, style = MaterialTheme.typography.labelMedium.copy( color = MaterialTheme.colorScheme.onBackground, ), diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicList.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicList.kt index 1e8e06f..fdb31c6 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicList.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicList.kt @@ -7,9 +7,6 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import kotlindictionarymultiplatform.composeapp.generated.resources.Res -import kotlindictionarymultiplatform.composeapp.generated.resources.description_subtitle -import org.jetbrains.compose.resources.stringResource @Composable fun TopicList( @@ -27,7 +24,7 @@ fun TopicList( TopicCard( topic = topic.name, itemTopic = topic, - subtitle = stringResource(Res.string.description_subtitle), + description = topic.description, isBookmarked = isBookmarked, onBookmarkClick = { onBookmarkClick(index) }, onCardClick = { onTopicClick(topic.name) }, diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicUiState.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicUiState.kt index b11f3e1..82552d0 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicUiState.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicUiState.kt @@ -14,4 +14,5 @@ data class ItemTopic( val name: String, val initial: String, val isBookmarked: Boolean, + val description: String, ) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt index 9a0b5c9..414ac0a 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt @@ -24,7 +24,7 @@ class TopicViewModel( } } - private fun fetchTopicList() { + private suspend fun fetchTopicList() { _uiState.value = UiState.Success(TopicUi(isLoading = true)) repository.getTopics().fold( ifLeft = { UiState.Error(it) }, @@ -67,6 +67,7 @@ class TopicViewModel( name = topic.name, initial = topic.name.first().uppercase(), isBookmarked = bookmarks.getOrNull(index) ?: false, + description = topic.description, ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f76afe7..fba06fc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,6 +29,7 @@ jetbrainsKotlinJvm = "2.1.10" kotlinStdlib = "2.1.10" runner = "1.6.2" core = "1.6.1" +uiToolingPreviewAndroid = "1.8.2" [libraries] androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" } @@ -67,6 +68,7 @@ koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlinStdlib" } androidx-runner = { group = "androidx.test", name = "runner", version.ref = "runner" } androidx-core = { group = "androidx.test", name = "core", version.ref = "core" } +androidx-ui-tooling-preview-android = { group = "androidx.compose.ui", name = "ui-tooling-preview-android", version.ref = "uiToolingPreviewAndroid" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } From 3c36718c5ac13f244bb7ddeb2e414dfaa622ec1d Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Tue, 17 Jun 2025 17:03:22 +0530 Subject: [PATCH 02/10] Migrate ktor-client-okhttp dependency to version catalog --- composeApp/build.gradle.kts | 2 +- gradle/libs.versions.toml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 24bebdb..f29f12f 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -87,7 +87,7 @@ kotlin { implementation(libs.koin.androidx.compose) implementation(libs.generativeai) implementation(compose.uiTooling) - implementation("io.ktor:ktor-client-okhttp:2.3.7") + implementation(libs.ktor.client.okhttp) } commonMain.dependencies { implementation(compose.runtime) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fba06fc..5f49d58 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -57,6 +57,7 @@ ktor-client-android = { module = "io.ktor:ktor-client-android" } ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation" } ktor-client-logging = { module = "io.ktor:ktor-client-logging" } ktor-client-mock = { module = "io.ktor:ktor-client-mock" } +ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor-bom" } ktor-client-serialization = { module = "io.ktor:ktor-client-serialization" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json" } koin-android = { module = "io.insert-koin:koin-android" } From 2095ac12c35e46437be811a80fd5b8f48101746d Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Tue, 17 Jun 2025 17:08:35 +0530 Subject: [PATCH 03/10] Update README with progress on v0.2.0 roadmap --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8a31fc3..b2ad875 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,12 @@ Access the latest APK for Kotlin Dictionary from the link below. ### Roadmap v0.2.0 -- [ ] Assign unique IDs to objects and enforce consistent ordering logic +- [x] Assign unique IDs to objects and enforce consistent ordering logic - [ ] Correct usage of visibility modifiers across the codebase - [ ] Introduce common `@Preview` annotations for reusable Composable previews - [x] Add code block for syntax display on the `Detail Screen` - [ ] Implement caching on the `Detail Screen` to store previously viewed topic data -- [ ] Implement dynamic topic loading in `TopicRepository` to support scalability +- [x] Implement dynamic topic loading in `TopicRepository` to support scalability - [ ] Integrate Room database to persist bookmark states - [ ] Add a `Home Page` for navigation - [ ] Add a `Quiz Page` to host topic-based quizzes From 301a529ecfa689792cf9d65cb1ac3e8eb6b5640d Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh <115153463+yesshreyes@users.noreply.github.com> Date: Wed, 18 Jun 2025 17:07:36 +0530 Subject: [PATCH 04/10] Changed `Get APK` button color --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2ad875..6166433 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Download the APK Access the latest APK for Kotlin Dictionary from the link below. -[![Get APK](https://img.shields.io/badge/Get%20APK-maroon?style=for-the-badge&logo=android&logoColor=white)](https://github.com/DevelopersBreach/kotlin-dictionary-multiplatform/releases/download/v0.1.0/app-release-v0.1.0.apk) +[![Get APK](https://img.shields.io/badge/Get%20APK-%23B125EA?style=for-the-badge&logo=android&logoColor=white)](https://github.com/DevelopersBreach/kotlin-dictionary-multiplatform/releases/download/v0.1.0/app-release-v0.1.0.apk) --- From e7cc4869c70725744b4183fa6ba902a4bd5bd3db Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Wed, 18 Jun 2025 22:33:41 +0530 Subject: [PATCH 05/10] Remove bookmark functionality from topic screen --- .../previews/PreviewUtils.kt | 1 - .../previews/topic/TopicScreenPreview.kt | 2 -- .../ui/screens/topic/TopicCard.kt | 27 ------------------- .../ui/screens/topic/TopicList.kt | 9 ++----- .../ui/screens/topic/TopicScreen.kt | 2 -- .../ui/screens/topic/TopicScreenUI.kt | 4 --- .../ui/screens/topic/TopicUiState.kt | 2 -- .../ui/screens/topic/TopicViewModel.kt | 18 ++----------- 8 files changed, 4 insertions(+), 61 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt b/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt index 68061b4..aa9a59b 100644 --- a/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt +++ b/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt @@ -81,7 +81,6 @@ internal fun sampleTopicUiList(): List { ItemTopic( name = topic.name, initial = topic.name.firstOrNull()?.uppercase() ?: "", - isBookmarked = true, description = topic.description, ) } diff --git a/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/topic/TopicScreenPreview.kt b/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/topic/TopicScreenPreview.kt index 7d6fd86..c34fd62 100644 --- a/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/topic/TopicScreenPreview.kt +++ b/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/topic/TopicScreenPreview.kt @@ -12,10 +12,8 @@ private fun TopicScreenPreview() { KotlinDictionaryTheme { TopicScreenUI( topics = sampleTopicUiList(), - bookmarkedStates = listOf(), searchQuery = "Search", onQueryChange = { }, - onBookmarkClick = { }, onTopicClick = { }, ) } diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt index 2a4d333..9998c81 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicCard.kt @@ -13,9 +13,6 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Bookmark -import androidx.compose.material.icons.outlined.BookmarkBorder import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -23,21 +20,14 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.developersbreach.designsystem.components.KdIconButton import com.developersbreach.designsystem.components.KdSurface import com.developersbreach.designsystem.components.KdText -import kotlindictionarymultiplatform.composeapp.generated.resources.Res -import kotlindictionarymultiplatform.composeapp.generated.resources.add_bookmark -import kotlindictionarymultiplatform.composeapp.generated.resources.remove_bookmark -import org.jetbrains.compose.resources.stringResource @Composable fun TopicCard( itemTopic: ItemTopic, topic: String, description: String, - isBookmarked: Boolean, - onBookmarkClick: () -> Unit, onCardClick: () -> Unit, ) { KdSurface( @@ -99,23 +89,6 @@ fun TopicCard( overflow = TextOverflow.Ellipsis, ) } - - KdIconButton( - modifier = Modifier, - iconModifier = Modifier, - onClick = onBookmarkClick, - imageVector = if (isBookmarked) { - Icons.Outlined.BookmarkBorder - } else { - Icons.Filled.Bookmark - }, - contentDescription = if (isBookmarked) { - stringResource(Res.string.remove_bookmark) - } else { - stringResource(Res.string.add_bookmark) - }, - tint = MaterialTheme.colorScheme.primary, - ) } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicList.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicList.kt index fdb31c6..c916049 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicList.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicList.kt @@ -3,7 +3,7 @@ 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.itemsIndexed +import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -11,22 +11,17 @@ import androidx.compose.ui.unit.dp @Composable fun TopicList( topics: List, - bookmarkedStates: List, - onBookmarkClick: (Int) -> Unit, onTopicClick: (String) -> Unit, ) { LazyColumn( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(bottom = 40.dp), ) { - itemsIndexed(topics) { index, topic -> - val isBookmarked = bookmarkedStates.getOrNull(index) ?: true + items(topics) { topic -> TopicCard( topic = topic.name, itemTopic = topic, description = topic.description, - isBookmarked = isBookmarked, - onBookmarkClick = { onBookmarkClick(index) }, onCardClick = { onTopicClick(topic.name) }, ) } diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreen.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreen.kt index 0647ad9..efebf55 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreen.kt @@ -17,10 +17,8 @@ fun TopicScreen( ) { data -> TopicScreenUI( topics = data.filteredTopics, - bookmarkedStates = data.bookmarkedStates, searchQuery = data.searchQuery, onQueryChange = viewModel::updateSearchQuery, - onBookmarkClick = viewModel::toggleBookmark, onTopicClick = onTopicClick, ) } diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreenUI.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreenUI.kt index 105aa93..770320b 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreenUI.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicScreenUI.kt @@ -12,10 +12,8 @@ import com.developersbreach.designsystem.components.KdScaffold @Composable fun TopicScreenUI( topics: List, - bookmarkedStates: List, searchQuery: String, onQueryChange: (String) -> Unit, - onBookmarkClick: (Int) -> Unit, onTopicClick: (String) -> Unit, ) { KdScaffold( @@ -34,8 +32,6 @@ fun TopicScreenUI( Spacer(modifier = Modifier.height(8.dp)) TopicList( topics = topics, - bookmarkedStates = bookmarkedStates, - onBookmarkClick = onBookmarkClick, onTopicClick = onTopicClick, ) } diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicUiState.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicUiState.kt index 82552d0..d0bd8cd 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicUiState.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicUiState.kt @@ -6,13 +6,11 @@ data class TopicUi( val isLoading: Boolean = false, val topics: List = emptyList(), val searchQuery: String = "", - val bookmarkedStates: List = emptyList(), val filteredTopics: List = emptyList(), ) data class ItemTopic( val name: String, val initial: String, - val isBookmarked: Boolean, val description: String, ) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt index 414ac0a..18122dc 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt @@ -30,8 +30,7 @@ class TopicViewModel( ifLeft = { UiState.Error(it) }, ifRight = { list -> rawTopics = list.sortedBy { it.name.lowercase() } - val initialBookmarks = List(rawTopics.size) { true } - applyFilters(rawTopics, (_uiState.value as UiState.Success).data.searchQuery, initialBookmarks) + applyFilters(rawTopics, (_uiState.value as UiState.Success).data.searchQuery) }, ) } @@ -39,23 +38,12 @@ class TopicViewModel( fun updateSearchQuery( newQuery: String, ) { - val bookmarks = (_uiState.value as UiState.Success).data.bookmarkedStates - applyFilters(rawTopics, newQuery, bookmarks) - } - - fun toggleBookmark( - index: Int, - ) { - val current = (_uiState.value as UiState.Success).data.bookmarkedStates - if (index !in current.indices) return - val updated = current.toMutableList().apply { this[index] = !this[index] } - applyFilters(rawTopics, (_uiState.value as UiState.Success).data.searchQuery, updated) + applyFilters(rawTopics, newQuery) } private fun applyFilters( topics: List, query: String, - bookmarks: List, ) { val filtered = topics .withIndex() @@ -66,7 +54,6 @@ class TopicViewModel( ItemTopic( name = topic.name, initial = topic.name.first().uppercase(), - isBookmarked = bookmarks.getOrNull(index) ?: false, description = topic.description, ) } @@ -76,7 +63,6 @@ class TopicViewModel( isLoading = false, searchQuery = query, topics = topics, - bookmarkedStates = bookmarks, filteredTopics = filtered, ), ) From 911a04c5275d0ec695ee66f8b206926e5a797e40 Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Wed, 18 Jun 2025 22:52:09 +0530 Subject: [PATCH 06/10] Handle null topic names and descriptions --- .../previews/PreviewUtils.kt | 6 +++--- .../data/topic/model/Topic.kt | 4 ++-- .../ui/screens/topic/TopicViewModel.kt | 15 +++++++-------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt b/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt index aa9a59b..75e2f26 100644 --- a/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt +++ b/composeApp/src/androidMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/previews/PreviewUtils.kt @@ -79,9 +79,9 @@ private fun sampleTopicList(): List { internal fun sampleTopicUiList(): List { return sampleTopicList().map { topic -> ItemTopic( - name = topic.name, - initial = topic.name.firstOrNull()?.uppercase() ?: "", - description = topic.description, + name = topic.name ?: "", + initial = topic.name?.firstOrNull()?.uppercase() ?: "", + description = topic.description ?: "", ) } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/Topic.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/Topic.kt index 873d1d3..00bdb0c 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/Topic.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/Topic.kt @@ -4,6 +4,6 @@ import kotlinx.serialization.Serializable @Serializable data class Topic( - val name: String, - val description: String, + val name: String?, + val description: String?, ) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt index 18122dc..1476fcd 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/ui/screens/topic/TopicViewModel.kt @@ -29,7 +29,7 @@ class TopicViewModel( repository.getTopics().fold( ifLeft = { UiState.Error(it) }, ifRight = { list -> - rawTopics = list.sortedBy { it.name.lowercase() } + rawTopics = list.sortedBy { it.name?.lowercase() ?: "" } applyFilters(rawTopics, (_uiState.value as UiState.Success).data.searchQuery) }, ) @@ -46,15 +46,14 @@ class TopicViewModel( query: String, ) { val filtered = topics - .withIndex() - .filter { (_, topic) -> - topic.name.contains(query, ignoreCase = true) + .filter { topic -> + topic.name?.contains(query, ignoreCase = true) == true } - .map { (index, topic) -> + .map { topic -> ItemTopic( - name = topic.name, - initial = topic.name.first().uppercase(), - description = topic.description, + name = topic.name ?: "", + initial = topic.name?.firstOrNull()?.uppercase() ?: "", + description = topic.description ?: "", ) } From 27c9efe02eafebc4e00af21c6e006a00ecf20659 Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Wed, 18 Jun 2025 23:07:42 +0530 Subject: [PATCH 07/10] Refactor: Improve naming of Firestore data models --- .../data/topic/model/FirestoreResponse.kt | 14 +++++++------- .../data/topic/repository/TopicRepository.kt | 6 +++--- .../data/topic/utils/Mappers.kt | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt index 09cd176..2b56083 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt @@ -4,16 +4,16 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class FirestoreDocument( - @SerialName("fields") val fields: Map, +data class RawTopic( + @SerialName("fields") val fields: Map, ) @Serializable -data class FirestoreField( - @SerialName("stringValue") val stringValue: String, +data class RawField( + @SerialName("stringValue") val value: String, ) @Serializable -data class FirestoreResponse( - @SerialName("documents") val documents: List, -) \ No newline at end of file +data class Response( + @SerialName("documents") val topics: List, +) diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt index eebec49..48a2493 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt @@ -1,7 +1,7 @@ package com.developersbreach.kotlindictionarymultiplatform.data.topic.repository import arrow.core.Either -import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.FirestoreResponse +import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Response import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic import com.developersbreach.kotlindictionarymultiplatform.data.topic.utils.FirestoreConstants import com.developersbreach.kotlindictionarymultiplatform.data.topic.utils.toTopic @@ -22,8 +22,8 @@ object TopicRepository { suspend fun getTopics(): Either> { return Either.catch { - val response: FirestoreResponse = client.get(FirestoreConstants.TOPICS_URL).body() - response.documents.map { it.toTopic() } + val response: Response = client.get(FirestoreConstants.TOPICS_URL).body() + response.topics.map { it.toTopic() } } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt index 99f9297..02e915c 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt @@ -1,11 +1,11 @@ package com.developersbreach.kotlindictionarymultiplatform.data.topic.utils -import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.FirestoreDocument +import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.RawTopic import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic -fun FirestoreDocument.toTopic(): Topic { +fun RawTopic.toTopic(): Topic { return Topic( - name = fields["name"]?.stringValue.orEmpty(), - description = fields["description"]?.stringValue.orEmpty(), + name = fields["name"]?.value.orEmpty(), + description = fields["description"]?.value.orEmpty(), ) } \ No newline at end of file From fa724f5b8e0fe97974c5ad70d274ba680628b194 Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Wed, 18 Jun 2025 23:37:47 +0530 Subject: [PATCH 08/10] Refactor: Organize network related classes --- .../api/GeminiApiService.kt | 6 ++-- .../parser/GeminiJsonParser.kt | 2 +- .../request/GeminiPromptBuilder.kt | 2 +- .../network/topicSource/FirestoreConstants.kt | 6 ++++ .../detail/repository/DetailRepository.kt | 2 +- .../data/topic/model/FirestoreResponse.kt | 7 ++++ .../data/topic/repository/TopicRepository.kt | 35 ++++++++++++------- .../data/topic/utils/FirestoreConstants.kt | 6 ---- .../data/topic/utils/Mappers.kt | 11 ------ .../di/ApiModule.kt | 2 +- .../di/RepositoryModule.kt | 2 +- 11 files changed, 43 insertions(+), 38 deletions(-) rename composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/{ => detailsGenerator}/api/GeminiApiService.kt (92%) rename composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/{ => detailsGenerator}/parser/GeminiJsonParser.kt (96%) rename composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/{ => detailsGenerator}/request/GeminiPromptBuilder.kt (98%) create mode 100644 composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/topicSource/FirestoreConstants.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/FirestoreConstants.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/api/GeminiApiService.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/api/GeminiApiService.kt similarity index 92% rename from composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/api/GeminiApiService.kt rename to composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/api/GeminiApiService.kt index 3d7e2db..55ae629 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/api/GeminiApiService.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/api/GeminiApiService.kt @@ -1,8 +1,8 @@ -package com.developersbreach.kotlindictionarymultiplatform.core.network.api +package com.developersbreach.kotlindictionarymultiplatform.core.network.detailsGenerator.api import com.developersbreach.kotlindictionarymultiplatform.Log -import com.developersbreach.kotlindictionarymultiplatform.core.network.parser.GeminiJsonParser -import com.developersbreach.kotlindictionarymultiplatform.core.network.request.GeminiPromptBuilder +import com.developersbreach.kotlindictionarymultiplatform.core.network.detailsGenerator.parser.GeminiJsonParser +import com.developersbreach.kotlindictionarymultiplatform.core.network.detailsGenerator.request.GeminiPromptBuilder import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.KotlinTopicDetails import io.ktor.client.HttpClient import io.ktor.client.request.post diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/parser/GeminiJsonParser.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/parser/GeminiJsonParser.kt similarity index 96% rename from composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/parser/GeminiJsonParser.kt rename to composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/parser/GeminiJsonParser.kt index be4a28d..a386c9a 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/parser/GeminiJsonParser.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/parser/GeminiJsonParser.kt @@ -1,4 +1,4 @@ -package com.developersbreach.kotlindictionarymultiplatform.core.network.parser +package com.developersbreach.kotlindictionarymultiplatform.core.network.detailsGenerator.parser import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/request/GeminiPromptBuilder.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/request/GeminiPromptBuilder.kt similarity index 98% rename from composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/request/GeminiPromptBuilder.kt rename to composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/request/GeminiPromptBuilder.kt index ec755ea..06279ea 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/request/GeminiPromptBuilder.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/detailsGenerator/request/GeminiPromptBuilder.kt @@ -1,4 +1,4 @@ -package com.developersbreach.kotlindictionarymultiplatform.core.network.request +package com.developersbreach.kotlindictionarymultiplatform.core.network.detailsGenerator.request import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.buildJsonObject diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/topicSource/FirestoreConstants.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/topicSource/FirestoreConstants.kt new file mode 100644 index 0000000..78f6401 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/core/network/topicSource/FirestoreConstants.kt @@ -0,0 +1,6 @@ +package com.developersbreach.kotlindictionarymultiplatform.core.network.topicSource + +object FirestoreConstants { + private const val ROOT_URL = "https://firestore.googleapis.com/v1/projects/kotlin-dictionary/databases/(default)/documents" + const val TOPICS_URL = "$ROOT_URL/topics" +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/detail/repository/DetailRepository.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/detail/repository/DetailRepository.kt index 4c4a0d1..c38a547 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/detail/repository/DetailRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/detail/repository/DetailRepository.kt @@ -1,7 +1,7 @@ package com.developersbreach.kotlindictionarymultiplatform.data.detail.repository import arrow.core.Either -import com.developersbreach.kotlindictionarymultiplatform.core.network.api.GeminiApiService +import com.developersbreach.kotlindictionarymultiplatform.core.network.detailsGenerator.api.GeminiApiService import com.developersbreach.kotlindictionarymultiplatform.data.detail.model.KotlinTopicDetails import com.developersbreach.kotlindictionarymultiplatform.getOpenApiKey diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt index 2b56083..5e204f0 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt @@ -17,3 +17,10 @@ data class RawField( data class Response( @SerialName("documents") val topics: List, ) + +fun RawTopic.toTopic(): Topic { + return Topic( + name = fields["name"]?.value.orEmpty(), + description = fields["description"]?.value.orEmpty(), + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt index 48a2493..453e921 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt @@ -3,8 +3,8 @@ package com.developersbreach.kotlindictionarymultiplatform.data.topic.repository import arrow.core.Either import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Response import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic -import com.developersbreach.kotlindictionarymultiplatform.data.topic.utils.FirestoreConstants -import com.developersbreach.kotlindictionarymultiplatform.data.topic.utils.toTopic +import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.toTopic +import com.developersbreach.kotlindictionarymultiplatform.core.network.topicSource.FirestoreConstants import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.get @@ -12,18 +12,27 @@ import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json -object TopicRepository { +//object TopicRepository { +// +// private val client = HttpClient { +// install(ContentNegotiation) { +// json(Json { ignoreUnknownKeys = true }) +// } +// } +// +// suspend fun getTopics(): Either> { +// return Either.catch { +// val response: Response = client.get(FirestoreConstants.TOPICS_URL).body() +// response.topics.map { it.toTopic() } +// } +// } +//} - private val client = HttpClient { - install(ContentNegotiation) { - json(Json { ignoreUnknownKeys = true }) - } - } - - suspend fun getTopics(): Either> { - return Either.catch { - val response: Response = client.get(FirestoreConstants.TOPICS_URL).body() +class TopicRepository( + private val httpClient: HttpClient +){ + suspend fun getTopics(): Either> = Either.catch { + val response: Response = httpClient.get(FirestoreConstants.TOPICS_URL).body() response.topics.map { it.toTopic() } - } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/FirestoreConstants.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/FirestoreConstants.kt deleted file mode 100644 index c1e1579..0000000 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/FirestoreConstants.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.developersbreach.kotlindictionarymultiplatform.data.topic.utils - -object FirestoreConstants { - const val TOPICS_URL = - "https://firestore.googleapis.com/v1/projects/kotlin-dictionary/databases/(default)/documents/topics" -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt deleted file mode 100644 index 02e915c..0000000 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/utils/Mappers.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.developersbreach.kotlindictionarymultiplatform.data.topic.utils - -import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.RawTopic -import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Topic - -fun RawTopic.toTopic(): Topic { - return Topic( - name = fields["name"]?.value.orEmpty(), - description = fields["description"]?.value.orEmpty(), - ) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/di/ApiModule.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/di/ApiModule.kt index 571ecb0..656e70a 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/di/ApiModule.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/di/ApiModule.kt @@ -1,6 +1,6 @@ package com.developersbreach.kotlindictionarymultiplatform.di -import com.developersbreach.kotlindictionarymultiplatform.core.network.api.GeminiApiService +import com.developersbreach.kotlindictionarymultiplatform.core.network.detailsGenerator.api.GeminiApiService import org.koin.dsl.module internal val apiModule = module { diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/di/RepositoryModule.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/di/RepositoryModule.kt index 18a16fc..69838e5 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/di/RepositoryModule.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/di/RepositoryModule.kt @@ -9,6 +9,6 @@ internal val repositoryModule = module { DetailRepository(get()) } single { - TopicRepository + TopicRepository(get()) } } \ No newline at end of file From bd85fab9ba13e821f09d1d33bc73d92137edbc7e Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Wed, 18 Jun 2025 23:40:02 +0530 Subject: [PATCH 09/10] Refactor `TopicRepository` to use constructor injection for `HttpClient`. --- .../data/topic/repository/TopicRepository.kt | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt index 453e921..02880ea 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt @@ -8,31 +8,14 @@ import com.developersbreach.kotlindictionarymultiplatform.core.network.topicSour import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.get -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.serialization.kotlinx.json.json -import kotlinx.serialization.json.Json - -//object TopicRepository { -// -// private val client = HttpClient { -// install(ContentNegotiation) { -// json(Json { ignoreUnknownKeys = true }) -// } -// } -// -// suspend fun getTopics(): Either> { -// return Either.catch { -// val response: Response = client.get(FirestoreConstants.TOPICS_URL).body() -// response.topics.map { it.toTopic() } -// } -// } -//} class TopicRepository( - private val httpClient: HttpClient -){ - suspend fun getTopics(): Either> = Either.catch { + private val httpClient: HttpClient, +) { + suspend fun getTopics(): Either> { + return Either.catch { val response: Response = httpClient.get(FirestoreConstants.TOPICS_URL).body() response.topics.map { it.toTopic() } + } } } \ No newline at end of file From 451822eb28604ec1dbdf0c8ecb6b9a42451de696 Mon Sep 17 00:00:00 2001 From: Shreyas Deshmukh Date: Thu, 19 Jun 2025 12:41:19 +0530 Subject: [PATCH 10/10] Refactor Firestore response handling and remove unused strings --- README.md | 2 +- .../composeResources/values/string.xml | 2 -- ...{FirestoreResponse.kt => TopicResponse.kt} | 20 ++++++++++++------- .../data/topic/repository/TopicRepository.kt | 6 +++--- 4 files changed, 17 insertions(+), 13 deletions(-) rename composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/{FirestoreResponse.kt => TopicResponse.kt} (57%) diff --git a/README.md b/README.md index 6166433..f66677d 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Access the latest APK for Kotlin Dictionary from the link below. - [x] Add code block for syntax display on the `Detail Screen` - [ ] Implement caching on the `Detail Screen` to store previously viewed topic data - [x] Implement dynamic topic loading in `TopicRepository` to support scalability -- [ ] Integrate Room database to persist bookmark states +- [ ] Add bookmark feature for topic cards to allow users to save and revisit important topics - [ ] Add a `Home Page` for navigation - [ ] Add a `Quiz Page` to host topic-based quizzes - [ ] Add a button in `DetailScreen` to attempt a quiz for that topic diff --git a/composeApp/src/commonMain/composeResources/values/string.xml b/composeApp/src/commonMain/composeResources/values/string.xml index 4afcf80..beb55cb 100644 --- a/composeApp/src/commonMain/composeResources/values/string.xml +++ b/composeApp/src/commonMain/composeResources/values/string.xml @@ -23,8 +23,6 @@ Topics Description need to be added Icon - Remove bookmark - Add bookmark Search Kotlin terms... Search \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/TopicResponse.kt similarity index 57% rename from composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt rename to composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/TopicResponse.kt index 5e204f0..16e5002 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/FirestoreResponse.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/model/TopicResponse.kt @@ -3,24 +3,30 @@ package com.developersbreach.kotlindictionarymultiplatform.data.topic.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +@Serializable +data class TopicResponse( + @SerialName("documents") val topics: List, +) + @Serializable data class RawTopic( - @SerialName("fields") val fields: Map, + @SerialName("fields") val fields: TopicFields, ) @Serializable -data class RawField( - @SerialName("stringValue") val value: String, +data class TopicFields( + @SerialName("name") val name: RawField, + @SerialName("description") val description: RawField, ) @Serializable -data class Response( - @SerialName("documents") val topics: List, +data class RawField( + @SerialName("stringValue") val value: String, ) fun RawTopic.toTopic(): Topic { return Topic( - name = fields["name"]?.value.orEmpty(), - description = fields["description"]?.value.orEmpty(), + name = fields.name.value, + description = fields.description.value, ) } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt index 02880ea..16192dd 100644 --- a/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt +++ b/composeApp/src/commonMain/kotlin/com/developersbreach/kotlindictionarymultiplatform/data/topic/repository/TopicRepository.kt @@ -1,7 +1,7 @@ package com.developersbreach.kotlindictionarymultiplatform.data.topic.repository import arrow.core.Either -import com.developersbreach.kotlindictionarymultiplatform.data.topic.model.Response +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 @@ -14,8 +14,8 @@ class TopicRepository( ) { suspend fun getTopics(): Either> { return Either.catch { - val response: Response = httpClient.get(FirestoreConstants.TOPICS_URL).body() - response.topics.map { it.toTopic() } + val topicResponse: TopicResponse = httpClient.get(FirestoreConstants.TOPICS_URL).body() + topicResponse.topics.map { it.toTopic() } } } } \ No newline at end of file