Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
07a5e8e
move splash screen to new package
HendSayed25 Sep 29, 2025
b8ae948
move bottomSheet to to new package
HendSayed25 Sep 29, 2025
d5a8afc
add dependencies for coil and ktor
HendSayed25 Sep 29, 2025
8e1d30d
add onboarding repo and entity
HendSayed25 Sep 29, 2025
0cf480f
add onboarding repo implementation , dto and response
HendSayed25 Sep 29, 2025
c996d1b
add safe call function , constants object and ktor setup
HendSayed25 Sep 29, 2025
1daa939
add state management for onboarding screen
HendSayed25 Sep 29, 2025
dcbf8aa
update screen and OnBoardingItem with view model
HendSayed25 Sep 29, 2025
a992b37
Fix: Add base URL and remove comment in NetworkConstants
HendSayed25 Sep 29, 2025
a3e82db
Merge remote-tracking branch 'origin/development' into feature/splash…
HendSayed25 Sep 29, 2025
49159d8
Refactor: Remove unused dependencies and versions
HendSayed25 Oct 1, 2025
b961839
Merge remote-tracking branch 'origin/development' into feature/splash…
HendSayed25 Oct 3, 2025
1a4e562
feat: Integrate NetworkModule and dependencies
HendSayed25 Oct 3, 2025
2e4af5f
feat: Update Ktor and add new dependencies
HendSayed25 Oct 6, 2025
8a893c6
feat: Implement platform-specific Ktor HTTP engines
HendSayed25 Oct 6, 2025
0d1871d
Chore: Update base URL and HttpClient engine
HendSayed25 Oct 6, 2025
1060f35
Refactor: Remove OnboardingResponse wrapper
HendSayed25 Oct 6, 2025
51367f1
feat: Replace AccountSetupCategoryScreen with OnBoardingScreen
HendSayed25 Oct 6, 2025
0a24af5
Refactor: Improve Onboarding screen UI and structure
HendSayed25 Oct 8, 2025
eb306ff
Apply consistent naming and updates to Onboarding feature
HendSayed25 Oct 8, 2025
31d12d0
Fix: Rename OnBoardingScreen to OnboardingScreen
HendSayed25 Oct 8, 2025
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
23 changes: 21 additions & 2 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
Expand All @@ -11,6 +10,7 @@ plugins {
alias(libs.plugins.google.services)
alias(libs.plugins.firebase.crashlytics)
alias(libs.plugins.ksp)
alias(libs.plugins.kotlinx.serialization)
}

kotlin {
Expand Down Expand Up @@ -41,6 +41,11 @@ kotlin {
implementation(libs.firebase.analytics)
implementation(project.dependencies.platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics.ktx)

implementation(libs.ktor.client.okhttp)
implementation(libs.ktor.client.cio)
implementation(libs.ktor.client.android)

}
commonMain.dependencies {
implementation(compose.runtime)
Expand All @@ -60,15 +65,29 @@ kotlin {
api(libs.koin.annotations)
implementation(libs.koin.compose)
implementation(libs.koin.compose.viewmodel)

implementation(libs.ktor.client.core)
implementation(libs.ktor.serialization.kotlinx.json)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.client.logging)

implementation(libs.bundles.coil)


}
commonTest.dependencies {
implementation(libs.kotlin.test)
}

iosMain.dependencies {
implementation(libs.ktor.client.darwin)
}
}

sourceSets.named("commonMain").configure {
kotlin.srcDir("build/generated/ksp/metadata/commonMain/kotlin")
}

}

ksp {
Expand Down Expand Up @@ -107,11 +126,11 @@ android {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

}

dependencies {
debugImplementation(compose.uiTooling)
add("kspCommonMainMetadata", libs.koin.ksp.compiler)

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.example.project.di

import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.cio.CIO

actual fun getHttpEngine(): HttpClientEngine = CIO.create()
4 changes: 2 additions & 2 deletions composeApp/src/commonMain/kotlin/org/example/project/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package org.example.project

import androidx.compose.runtime.Composable
import org.example.project.presentation.designsystem.textstyle.AppTheme
import org.example.project.presentation.ui.screens.setupScreens.AccountSetupCategoryScreen
import org.example.project.presentation.screens.onboarding.OnboardingScreen
import org.jetbrains.compose.ui.tooling.preview.Preview

@Composable
@Preview
fun App() {
AppTheme {
AccountSetupCategoryScreen()
OnboardingScreen()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.example.project.data.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class OnboardingDto(
@SerialName("id")
val id : String,
@SerialName("title")
val title : String,
@SerialName("imageRes")
val imageUrl : String,
@SerialName("description")
val description : String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.example.project.data.mapper

import org.example.project.data.dto.OnboardingDto
import org.example.project.domain.entity.OnboardingItem

fun OnboardingDto.toEntity() : OnboardingItem =
OnboardingItem(
imageRes = imageUrl,
title = title,
description = description
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.example.project.data.repository

import io.ktor.client.HttpClient
import io.ktor.client.request.get
import org.example.project.data.dto.OnboardingDto
import org.example.project.data.mapper.toEntity
import org.example.project.data.utils.NetworkConstants.ONBOARDING_END_POINT
import org.example.project.data.utils.safeApiCall
import org.example.project.domain.entity.OnboardingItem
import org.example.project.domain.repository.OnboardingRepository
import org.koin.core.annotation.Provided
import org.koin.core.annotation.Single


@Single(binds = [OnboardingRepository::class])
class OnboardingRepositoryImp(
@Provided private val httpClient: HttpClient
) : OnboardingRepository {

override suspend fun getOnboardingData(): List<OnboardingItem> {
return safeApiCall<List<OnboardingDto>> {
httpClient.get(ONBOARDING_END_POINT)
}.map { it.toEntity() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.example.project.data.utils

object NetworkConstants {
const val ONBOARDING_END_POINT = "/onboarding"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package org.example.project.data.utils

import io.ktor.client.call.body
import io.ktor.client.statement.HttpResponse
import io.ktor.http.HttpStatusCode
import io.ktor.util.network.UnresolvedAddressException
import kotlinx.io.IOException

internal suspend inline fun <reified T> safeApiCall(
execute: suspend () -> HttpResponse
): T {
val result = try {
execute()
} catch (exception: IOException) {
logError(SAFE_API_CALL_TAG, "IOException", exception.message.toString())

} catch (exception: UnresolvedAddressException) {
logError(SAFE_API_CALL_TAG, "UnresolvedAddressException", exception.message.toString())
} catch (exception: Exception) {
logError(SAFE_API_CALL_TAG, "Unknown exception", exception.message.toString())
}

return handleResponseStatusCode(result as HttpResponse)
}

private suspend inline fun <reified T> handleResponseStatusCode(result: HttpResponse): T {
return when (result.status.value) {
in 200..299 -> {
result.body<T>()
}

in 400..499 -> {
when (result.status) {
HttpStatusCode.Unauthorized -> {
logError(
HANDLE_ERROR_STATUS_TAG,
"Unauthorized",
"Not authorized to do this action"
)
throw Exception()
}

HttpStatusCode.NotFound -> {
logError(
HANDLE_ERROR_STATUS_TAG,
"Not found",
"the resource you requested could not be found"
)
throw Exception()
}

else -> {
logError(
HANDLE_ERROR_STATUS_TAG,
"Unknown 400s status code ${result.status.value}",
"An error with status code ${result.status.value} happened"
)
throw Exception()
}
}
}

in 500..599 -> {
logError(
HANDLE_ERROR_STATUS_TAG,
"Server error",
"An error occurred on the server side"
)
throw Exception()
}

else -> {
logError(
HANDLE_ERROR_STATUS_TAG,
"Unknown status code ${result.status.value}",
"An error with status code ${result.status.value} happened"
)
throw Exception()
}
}
}

private fun logError(
tag: String,
type: String,
message: String
) {
println("$tag----------- : $type $message")
}

private const val SAFE_API_CALL_TAG = "safeApiCall"
private const val HANDLE_ERROR_STATUS_TAG = "handleErrorStatus"
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.example.project.di

import io.ktor.client.HttpClient
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger
import io.ktor.client.plugins.logging.Logging
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
import org.koin.core.annotation.Module
import org.koin.core.annotation.Single

//192.168.1.15
@Module
class NetworkModule {
@Single
fun provideHttpClient(): HttpClient {
return HttpClient(engine = getHttpEngine()) {
defaultRequest {
url("http://10.0.2.2:8085/")
}

install(Logging) {
level = LogLevel.ALL
logger = object : Logger {
override fun log(message: String) {
println(message)
}
}
}

install(HttpTimeout) {
connectTimeoutMillis = TIME_OUT_INTERVAL_MILLI
requestTimeoutMillis = TIME_OUT_INTERVAL_MILLI
}

install(ContentNegotiation) {
json(
Json {
isLenient = true
ignoreUnknownKeys = true
}
)
}
}
}

private companion object {
const val TIME_OUT_INTERVAL_MILLI = 10_000L
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.example.project.di

import io.ktor.client.engine.HttpClientEngine

expect fun getHttpEngine(): HttpClientEngine
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import org.koin.ksp.generated.module
fun initKoin(config: KoinAppDeclaration? = null) {
startKoin {
config?.invoke(this)
modules(CraftoModule().module)
modules(CraftoModule().module, NetworkModule().module)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.example.project.domain.entity

data class OnboardingItem(
val imageRes : String,
val title : String,
val description : String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.example.project.domain.repository

import org.example.project.domain.entity.OnboardingItem

interface OnboardingRepository {
suspend fun getOnboardingData(): List<OnboardingItem>
}
Loading