From 5c4c9804ee0571ab19d70ee9d50a61c0956ae6a2 Mon Sep 17 00:00:00 2001 From: lamtruong28 Date: Thu, 14 May 2026 23:21:19 +0700 Subject: [PATCH] Revert commit eacb784 --- build.gradle.kts | 1 - composeApp/build.gradle.kts | 18 ++--- .../src/androidMain/AndroidManifest.xml | 5 +- .../app/torquevaultkmp/AppStorage.android.kt | 33 -------- .../app/torquevaultkmp/MainActivity.kt | 3 +- .../app/torquevaultkmp/MyApplication.kt | 17 ----- .../torquevaultkmp/NetworkEngine.android.kt | 75 ------------------ .../app/torquevaultkmp/di/PlatformModules.kt | 10 --- .../app/torquevaultkmp/App.kt | 17 ++--- .../app/torquevaultkmp/AppViewModel.kt | 27 ------- .../app/torquevaultkmp/NetworkEngine.kt | 7 -- .../app/torquevaultkmp/components/AppBar.kt | 6 ++ .../data/network/NetworkModule.kt | 25 ------ .../data/network/api/AuthApi.kt | 50 ------------ .../data/network/api/Endpoints.kt | 8 -- .../data/network/api/WorkOrderApi.kt | 27 ------- .../data/network/dto/AuthDto.kt | 12 --- .../data/network/dto/WorkOrderDto.kt | 34 --------- .../data/network/mapper/AuthMapper.kt | 24 ------ .../data/network/mapper/WorkOrderMapper.kt | 35 --------- .../data/repository/WorkOrderRepository.kt | 8 -- .../repository/WorkOrderRepositoryImpl.kt | 15 ---- .../torquevaultkmp/data/storage/AppStorage.kt | 13 ---- .../torquevaultkmp/data/storage/ReferKeys.kt | 7 -- .../data/storage/TokenManager.kt | 29 ------- .../app/torquevaultkmp/di/Koin.kt | 14 +--- .../app/torquevaultkmp/di/Modules.kt | 43 +---------- .../app/torquevaultkmp/domain/model/Auth.kt | 6 -- .../torquevaultkmp/domain/model/WorkOrder.kt | 44 ----------- .../domain/usecase/FetchWorkOrdersUseCase.kt | 10 --- .../app/torquevaultkmp/screens/MainScreen.kt | 4 +- .../screens/auth/AuthViewModel.kt | 43 ----------- .../screens/auth/LoginScreen.kt | 57 +++++++------- .../torquevaultkmp/screens/home/HomeScreen.kt | 46 +++-------- .../screens/home/HomeViewModel.kt | 33 -------- .../app/torquevaultkmp/utils/ApiException.kt | 11 --- .../app/torquevaultkmp/utils/NoOpAntilog.kt | 9 --- .../app/torquevaultkmp/AppStorage.ios.kt | 31 -------- .../app/torquevaultkmp/MainViewController.kt | 11 +-- .../app/torquevaultkmp/NetworkEngine.ios.kt | 76 ------------------- .../app/torquevaultkmp/di/PlatformModules.kt | 9 --- gradle/libs.versions.toml | 27 ++----- iosApp/iosApp/Info.plist | 14 ---- 43 files changed, 72 insertions(+), 922 deletions(-) delete mode 100644 composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppStorage.android.kt delete mode 100644 composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MyApplication.kt delete mode 100644 composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.android.kt delete mode 100644 composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/PlatformModules.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppViewModel.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/NetworkModule.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/AuthApi.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/Endpoints.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/WorkOrderApi.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/dto/AuthDto.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/dto/WorkOrderDto.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/mapper/AuthMapper.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/mapper/WorkOrderMapper.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/repository/WorkOrderRepository.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/repository/WorkOrderRepositoryImpl.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/AppStorage.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/ReferKeys.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/TokenManager.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/model/Auth.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/model/WorkOrder.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/usecase/FetchWorkOrdersUseCase.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/auth/AuthViewModel.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/home/HomeViewModel.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/utils/ApiException.kt delete mode 100644 composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/utils/NoOpAntilog.kt delete mode 100644 composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppStorage.ios.kt delete mode 100644 composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.ios.kt delete mode 100644 composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/PlatformModules.kt diff --git a/build.gradle.kts b/build.gradle.kts index d63f5ec..cf780d5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,5 +6,4 @@ plugins { alias(libs.plugins.composeMultiplatform) apply false alias(libs.plugins.composeCompiler) apply false alias(libs.plugins.kotlinMultiplatform) apply false - alias(libs.plugins.kotlin.serialization) apply false } \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index dd22698..9d616f4 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -6,7 +6,6 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.composeMultiplatform) alias(libs.plugins.composeCompiler) - alias(libs.plugins.kotlin.serialization) } kotlin { @@ -35,7 +34,6 @@ kotlin { implementation(libs.koin.androidx.compose) implementation(libs.koin.compose) implementation(libs.koin.compose.viewmodel) - implementation(libs.ktor.client.okhttp) } commonMain.dependencies { implementation(libs.compose.runtime) @@ -47,19 +45,19 @@ kotlin { implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.runtimeCompose) + implementation(project.dependencies.platform(libs.koin.bom)) + implementation(libs.koin.core) + implementation(libs.koin.compose) + implementation(libs.koin.compose.viewmodel) + implementation(libs.compose.material.icons.extended) implementation(libs.androidx.navigation.compose) - implementation(project.dependencies.platform(libs.koin.bom)) - implementation(libs.bundles.ktor) - - implementation(libs.koin.core) - implementation(libs.koin.compose.viewmodel) - - implementation(libs.logger.napier) +// implementation(libs.compose.material3.adaptiveNavigation3) +// implementation(libs.androidx.lifecycle.viewmodelNavigation3) } iosMain.dependencies { - implementation(libs.ktor.client.darwin) + } commonTest.dependencies { implementation(libs.kotlin.test) diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index cccba71..26403a7 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -1,10 +1,7 @@ - - + save(key: String, value: T) { - when (value) { - is String -> prefs.edit { putString(key, value) } - is Int -> prefs.edit { putInt(key, value) } - is Boolean -> prefs.edit { putBoolean(key, value) } - else -> throw IllegalArgumentException("Unsupported type") - } - } - - @Suppress("UNCHECKED_CAST") - override fun load(key: String, type: KClass): T? { - return when (type) { - String::class -> prefs.getString(key, null) as T? - Int::class -> prefs.getInt(key, -1) as T? - Boolean::class -> prefs.getBoolean(key, false) as T? - else -> throw IllegalArgumentException("Unsupported type") - } - } - - override fun remove(key: String) { - prefs.edit { remove(key) } - } -} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MainActivity.kt b/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MainActivity.kt index dc44c89..3367a93 100644 --- a/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MainActivity.kt +++ b/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MainActivity.kt @@ -6,13 +6,12 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview -import com.digitoolsolutions.app.torquevaultkmp.di.initKoin -import org.koin.android.ext.koin.androidContext class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) + setContent { App() } diff --git a/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MyApplication.kt b/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MyApplication.kt deleted file mode 100644 index 67eaa6b..0000000 --- a/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MyApplication.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp - -import android.app.Application -import com.digitoolsolutions.app.torquevaultkmp.di.initKoin -import io.github.aakira.napier.DebugAntilog -import io.github.aakira.napier.Napier -import org.koin.android.ext.koin.androidContext -class MyApplication : Application() { - override fun onCreate() { - super.onCreate() - // Add logger lib - Napier.base(DebugAntilog()) // NoOpAntilog() for release - initKoin { - androidContext(this@MyApplication) - } - } -} diff --git a/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.android.kt b/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.android.kt deleted file mode 100644 index 7851ff0..0000000 --- a/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.android.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp - -import com.digitoolsolutions.app.torquevaultkmp.data.network.api.AuthApi -import com.digitoolsolutions.app.torquevaultkmp.data.storage.ReferKeys -import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager -import com.digitoolsolutions.app.torquevaultkmp.utils.ForceLogoutException -import io.github.aakira.napier.Napier -import io.ktor.client.HttpClient -import io.ktor.client.engine.okhttp.OkHttp -import io.ktor.client.plugins.DefaultRequest -import io.ktor.client.plugins.HttpSend -import io.ktor.client.plugins.HttpTimeout -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.plugins.logging.DEFAULT -import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger -import io.ktor.client.plugins.logging.Logging -import io.ktor.client.plugins.plugin -import io.ktor.client.request.header -import io.ktor.http.HttpStatusCode -import io.ktor.serialization.kotlinx.json.json -import kotlinx.serialization.json.Json - -actual fun createPlatformHttpClient( - tokenManager: TokenManager, - authApi: AuthApi? -): HttpClient { - val client = HttpClient(OkHttp) { - install(ContentNegotiation) { - json(Json { - ignoreUnknownKeys = true - isLenient = true - }) - } - install(DefaultRequest) { - tokenManager.getAccessToken()?.let { - header("Authorization", "Bearer $it") - } - } - install(HttpTimeout) { requestTimeoutMillis = 30_000 } - install(Logging) { - logger = object : Logger { - override fun log(message: String) { - Napier.v(message, tag = "HTTP") - } - } - level = LogLevel.ALL // set LogLevel.NONE for release - } - } - - if (authApi != null) { - client.plugin(HttpSend).intercept { request -> - // Skip interceptor for refresh token request to avoid recursion - if (request.url.pathSegments.contains(ReferKeys.REFRESH_TOKEN)) { - return@intercept execute(request) - } - - val originalCall = execute(request) - if (originalCall.response.status == HttpStatusCode.Unauthorized) { - Napier.d(">>>>> [NetworkEngine.ios.kt] Access token expired, auto request refresh token...") - val newToken = authApi.refreshToken() - if (newToken != null) { - request.headers["Authorization"] = "Bearer $newToken" - execute(request) - } else { - tokenManager.clearTokens() - throw ForceLogoutException() - } - } else { - originalCall - } - } - } - return client -} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/PlatformModules.kt b/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/PlatformModules.kt deleted file mode 100644 index 1dcc915..0000000 --- a/composeApp/src/androidMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/PlatformModules.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.di - -import com.digitoolsolutions.app.torquevaultkmp.AppStoragePlatform -import com.digitoolsolutions.app.torquevaultkmp.data.storage.AppStorage -import org.koin.android.ext.koin.androidContext -import org.koin.dsl.module - -actual val platformModule = module { - single { AppStoragePlatform(androidContext()) } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/App.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/App.kt index 49774a6..e16b8ce 100644 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/App.kt +++ b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/App.kt @@ -1,25 +1,18 @@ package com.digitoolsolutions.app.torquevaultkmp -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview import com.digitoolsolutions.android.torquevault.ui.theme.TorqueVaultTheme import com.digitoolsolutions.app.torquevaultkmp.screens.MainScreen -import org.koin.compose.viewmodel.koinViewModel @Composable @Preview fun App() { - val viewModel = koinViewModel() - val savedTheme = viewModel.isDarkTheme.value - val isDarkTheme = savedTheme ?: isSystemInDarkTheme() - val isLoggedIn = viewModel.isLoggedIn() - - TorqueVaultTheme(darkTheme = isDarkTheme) { + TorqueVaultTheme { MainScreen( - isDarkTheme = isDarkTheme, - onThemeToggle = { viewModel.toggleTheme(it) }, - isLoggedIn = isLoggedIn + isLoggedIn = false, + isDarkTheme = true, + onThemeToggle = {} ) } -} +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppViewModel.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppViewModel.kt deleted file mode 100644 index 21eb3ea..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppViewModel.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp - -import androidx.compose.runtime.State -import androidx.compose.runtime.mutableStateOf -import androidx.lifecycle.ViewModel -import com.digitoolsolutions.app.torquevaultkmp.data.storage.AppStorage -import com.digitoolsolutions.app.torquevaultkmp.data.storage.ReferKeys -import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager -import com.digitoolsolutions.app.torquevaultkmp.data.storage.load - -class AppViewModel( - private val tokenManager: TokenManager, - private val storage: AppStorage -) : ViewModel() { - - private val _isDarkTheme = mutableStateOf(storage.load(ReferKeys.THEME)) - val isDarkTheme: State = _isDarkTheme - - fun isLoggedIn(): Boolean { - return tokenManager.getRefreshToken() != null - } - - fun toggleTheme(isDark: Boolean) { - _isDarkTheme.value = isDark - storage.save(ReferKeys.THEME, isDark) - } -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.kt deleted file mode 100644 index a684906..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp - -import com.digitoolsolutions.app.torquevaultkmp.data.network.api.AuthApi -import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager -import io.ktor.client.HttpClient - -expect fun createPlatformHttpClient(tokenManager: TokenManager, authApi: AuthApi?): HttpClient diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/components/AppBar.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/components/AppBar.kt index 7bf749f..08c71c5 100644 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/components/AppBar.kt +++ b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/components/AppBar.kt @@ -2,6 +2,11 @@ package com.digitoolsolutions.app.torquevaultkmp.components import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.displayCutout +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.union import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -25,6 +30,7 @@ import torquevaultkmp.composeapp.generated.resources.baseline_menu_24 import torquevaultkmp.composeapp.generated.resources.menu_item_accessibility import torquevaultkmp.composeapp.generated.resources.navigation_item_accessibility + @OptIn(ExperimentalMaterial3Api::class) @Composable fun AppBar( diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/NetworkModule.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/NetworkModule.kt deleted file mode 100644 index 3ee78c2..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/NetworkModule.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.network - -import com.digitoolsolutions.app.torquevaultkmp.utils.ApiException -import io.ktor.client.HttpClient -import io.ktor.client.plugins.HttpResponseValidator -import io.ktor.client.plugins.HttpTimeout -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.serialization.kotlinx.json.json -import kotlinx.serialization.json.Json - -object NetworkModule { - val client = HttpClient { - install(ContentNegotiation) { - json(Json { ignoreUnknownKeys = true }) - } - install(HttpTimeout) { requestTimeoutMillis = 30_000 } -// install(HttpResponseValidator) { -// validateResponse { response -> -// if (response.status.value >= 400) { -// throw ApiException(response.status, response.bodyAsText()) -// } -// } -// } - } -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/AuthApi.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/AuthApi.kt deleted file mode 100644 index 3c651e4..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/AuthApi.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.network.api - -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.LoginRequestDto -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.LoginResponseDto -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.RefreshRequestDto -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.RefreshResponseDto -import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager -import io.ktor.client.HttpClient -import io.ktor.client.call.body -import io.ktor.client.request.post -import io.ktor.client.request.setBody -import io.ktor.http.ContentType -import io.ktor.http.contentType - - -class AuthApi( - private val client: HttpClient, - private val baseUrl: String, - private val tokenManager: TokenManager -) { - suspend fun login(username: String, password: String): Boolean { - return try { - println(">>>>> Check LOGIN Api") - val res: LoginResponseDto = client.post("$baseUrl${Endpoints.AUTH_TOKEN}") { - contentType(ContentType.Application.Json) - setBody(LoginRequestDto(username, password)) - }.body() - println(res) - tokenManager.saveTokens(res.access, res.refresh) - true - } catch (e: Exception) { - println(">>>>> [AuthApi] Exception: $e") - false - } - } - suspend fun refreshToken(): String? { - val refreshToken = tokenManager.getRefreshToken() ?: return null - return try { - val res: RefreshResponseDto = client.post("$baseUrl${Endpoints.AUTH_REFRESH}") { - contentType(ContentType.Application.Json) - setBody(RefreshRequestDto(refreshToken)) - }.body() - tokenManager.saveTokens(res.access, refreshToken) - res.access - } catch (e: Exception) { - tokenManager.clearTokens() - null // refresh token invalid - } - } -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/Endpoints.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/Endpoints.kt deleted file mode 100644 index cbcb51c..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/Endpoints.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.network.api - -object Endpoints { - const val AUTH_TOKEN = "/token/" - const val AUTH_REFRESH = "/token/refresh/" - const val WORK_ORDERS = "/work-orders/able-v2/" - const val CONFIRM = "/confirm" -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/WorkOrderApi.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/WorkOrderApi.kt deleted file mode 100644 index 475dc3f..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/api/WorkOrderApi.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.network.api - -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.ConfirmRequestDto -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.WorkOrderDto -import io.ktor.client.HttpClient -import io.ktor.client.call.body -import io.ktor.client.request.get -import io.ktor.client.request.parameter -import io.ktor.client.request.post -import io.ktor.client.request.setBody - -class WorkOrderApi( - private val client: HttpClient, - private val baseUrl: String -) { - suspend fun fetchAbleWorkOrders(page: Int): List { - return client.get("$baseUrl${Endpoints.WORK_ORDERS}") { - parameter("page", page) - }.body() - } - - suspend fun confirm(mac: String, type: Int) { - client.post("$baseUrl${Endpoints.CONFIRM}") { - setBody(ConfirmRequestDto(mac, type)) - } - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/dto/AuthDto.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/dto/AuthDto.kt deleted file mode 100644 index 11c44f0..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/dto/AuthDto.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.network.dto - -import kotlinx.serialization.Serializable - -@Serializable -data class LoginRequestDto(val username: String, val password: String) -@Serializable -data class RefreshRequestDto(val refresh: String) -@Serializable -data class LoginResponseDto(val access: String, val refresh: String) -@Serializable -data class RefreshResponseDto(val access: String) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/dto/WorkOrderDto.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/dto/WorkOrderDto.kt deleted file mode 100644 index 20a4940..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/dto/WorkOrderDto.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.network.dto - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement - -@Serializable -data class WorkOrderDto( - @SerialName("wo_id") val id: String, - val status: String, - val car: String? = null, - val make: String?, - @SerialName("license_plate") val licensePlate: String, - val services: Map?, - @SerialName("meas_result") val measResult: Map?, - @SerialName("meas_history") val measHistory: Map?, - @SerialName("finished_at") val finishedAt: String?, - @SerialName("created_at") val createdAt: String, - @SerialName("updated_at") val updatedAt: String? = null, -) -@Serializable -data class ServiceDto( - val nut: Int?, - val status: Int, - val wheels: List?, - val wrench: String?, - @SerialName("torque_required") val torqueRequired: Double -) - -@Serializable -data class ConfirmRequestDto( - @SerialName("wrench_mac_addr") val mac: String, - val type: Int -) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/mapper/AuthMapper.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/mapper/AuthMapper.kt deleted file mode 100644 index 2bd94f8..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/mapper/AuthMapper.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.network.mapper - -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.LoginRequestDto -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.LoginResponseDto -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.RefreshRequestDto -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.RefreshResponseDto -import com.digitoolsolutions.app.torquevaultkmp.domain.model.LoginRequest -import com.digitoolsolutions.app.torquevaultkmp.domain.model.LoginResponse -import com.digitoolsolutions.app.torquevaultkmp.domain.model.RefreshRequest -import com.digitoolsolutions.app.torquevaultkmp.domain.model.RefreshResponse - -fun LoginRequestDto.toDomain(): LoginRequest { - return LoginRequest(username,password) -} - -fun LoginResponseDto.toDomain(): LoginResponse { - return LoginResponse(access, refresh) -} -fun RefreshRequestDto.toDomain(): RefreshRequest { - return RefreshRequest(refresh) -} -fun RefreshResponseDto.toDomain(): RefreshResponse { - return RefreshResponse(access) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/mapper/WorkOrderMapper.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/mapper/WorkOrderMapper.kt deleted file mode 100644 index c9304ba..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/network/mapper/WorkOrderMapper.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.network.mapper - -import com.digitoolsolutions.app.torquevaultkmp.data.network.dto.WorkOrderDto -import com.digitoolsolutions.app.torquevaultkmp.domain.model.Service -import com.digitoolsolutions.app.torquevaultkmp.domain.model.WorkOrder -import kotlinx.serialization.json.doubleOrNull -import kotlinx.serialization.json.jsonPrimitive - -fun WorkOrderDto.toDomain(): WorkOrder { - return WorkOrder( - id = id, - status = status, - car = car, - make = make, - licensePlate = licensePlate, - services = services?.mapValues { (_, dto) -> - Service( - nut = dto.nut, - status = dto.status, - wheels = dto.wheels, - wrench = dto.wrench, - torqueRequired = dto.torqueRequired - ) - }, - measResult = measResult?.mapValues { (_, value) -> - value.jsonPrimitive.doubleOrNull ?: Double.NaN - }, - measHistory = measHistory?.mapValues { (_, value) -> - value.jsonPrimitive.doubleOrNull ?: Double.NaN - }, - finishedAt = finishedAt, - createdAt = createdAt, - updatedAt = updatedAt - ) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/repository/WorkOrderRepository.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/repository/WorkOrderRepository.kt deleted file mode 100644 index 03bb541..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/repository/WorkOrderRepository.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.repository - -import com.digitoolsolutions.app.torquevaultkmp.domain.model.WorkOrder - -interface WorkOrderRepository { - suspend fun fetchAbleWorkOrders(page: Int): List - suspend fun confirm(mac: String, type: Int) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/repository/WorkOrderRepositoryImpl.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/repository/WorkOrderRepositoryImpl.kt deleted file mode 100644 index 85bcc2d..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/repository/WorkOrderRepositoryImpl.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.repository - -import com.digitoolsolutions.app.torquevaultkmp.data.network.api.WorkOrderApi -import com.digitoolsolutions.app.torquevaultkmp.data.network.mapper.toDomain -import com.digitoolsolutions.app.torquevaultkmp.domain.model.WorkOrder -class WorkOrderRepositoryImpl(private val api: WorkOrderApi) : WorkOrderRepository { - override suspend fun fetchAbleWorkOrders(page: Int): List { - val dto = api.fetchAbleWorkOrders(page) - return dto.map { it.toDomain() } - } - - override suspend fun confirm(mac: String, type: Int) { - api.confirm(mac, type) - } -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/AppStorage.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/AppStorage.kt deleted file mode 100644 index a434e12..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/AppStorage.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.storage - -import kotlin.reflect.KClass - -interface AppStorage { - fun save(key: String, value: T) - fun load(key: String, type: KClass): T? - fun remove(key: String) -} - -inline fun AppStorage.load(key: String): T? { - return load(key, T::class) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/ReferKeys.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/ReferKeys.kt deleted file mode 100644 index ac510fd..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/ReferKeys.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.storage - -object ReferKeys { - const val ACCESS_TOKEN = "access_token" - const val REFRESH_TOKEN = "refresh_token" - const val THEME = "theme" -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/TokenManager.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/TokenManager.kt deleted file mode 100644 index eeb3f3e..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/data/storage/TokenManager.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.data.storage - -class TokenManager(private val storage: AppStorage) { - private var accessToken: String? = null - private var refreshToken: String? = null - - fun getAccessToken(): String? { - if (accessToken == null) accessToken = storage.load(ReferKeys.ACCESS_TOKEN) - return accessToken - } - fun getRefreshToken(): String? { - if (refreshToken == null) refreshToken = storage.load(ReferKeys.REFRESH_TOKEN) - return refreshToken - } - - fun saveTokens(access: String, refresh: String) { - accessToken = access - refreshToken = refresh - storage.save(ReferKeys.ACCESS_TOKEN, access) - storage.save(ReferKeys.REFRESH_TOKEN, refresh) - } - - fun clearTokens() { - accessToken = null - refreshToken = null - storage.remove(ReferKeys.ACCESS_TOKEN) - storage.remove(ReferKeys.REFRESH_TOKEN) - } -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/Koin.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/Koin.kt index d45f6ab..4327187 100644 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/Koin.kt +++ b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/Koin.kt @@ -1,16 +1,4 @@ package com.digitoolsolutions.app.torquevaultkmp.di -import org.koin.core.context.startKoin -import org.koin.dsl.KoinAppDeclaration - -fun initKoin(config: KoinAppDeclaration? = null) { - startKoin { - config?.invoke(this) - modules( - appModule, - networkModule, - viewModelModule, - platformModule - ) - } +class Koin { } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/Modules.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/Modules.kt index e0ea012..8944053 100644 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/Modules.kt +++ b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/Modules.kt @@ -1,46 +1,5 @@ package com.digitoolsolutions.app.torquevaultkmp.di -import com.digitoolsolutions.app.torquevaultkmp.AppViewModel -import com.digitoolsolutions.app.torquevaultkmp.createPlatformHttpClient -import com.digitoolsolutions.app.torquevaultkmp.data.network.api.AuthApi -import com.digitoolsolutions.app.torquevaultkmp.data.network.api.WorkOrderApi -import com.digitoolsolutions.app.torquevaultkmp.data.repository.WorkOrderRepository -import com.digitoolsolutions.app.torquevaultkmp.data.repository.WorkOrderRepositoryImpl -import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager -import com.digitoolsolutions.app.torquevaultkmp.domain.usecase.FetchWorkOrdersUseCase -import com.digitoolsolutions.app.torquevaultkmp.screens.auth.AuthViewModel -import com.digitoolsolutions.app.torquevaultkmp.screens.home.HomeViewModel -import org.koin.core.module.Module -import org.koin.core.module.dsl.factoryOf -import org.koin.core.qualifier.named import org.koin.dsl.module -expect val platformModule: Module - -val appModule = module { - // TokenManager use AppStorage - single { TokenManager(get()) } -} - -val networkModule = module { - single { "http://digitoolsolutions.synology.me:3000/api/v1" } // base URL - - // Auth client (no interceptor to avoid circular dependency and recursion) - single(named("authClient")) { createPlatformHttpClient(get(), null) } - - // AuthApi uses the auth client - single { AuthApi(get(named("authClient")), get(), get()) } - - // Main client uses AuthApi for its interceptor - single { createPlatformHttpClient(get(), get()) } - - single { WorkOrderApi(get(), get()) } - single { WorkOrderRepositoryImpl(get()) } - factory { FetchWorkOrdersUseCase(get()) } -} - -val viewModelModule = module { - factoryOf(::HomeViewModel) - factoryOf(::AuthViewModel) - factoryOf(::AppViewModel) -} +val homeModule = module { } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/model/Auth.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/model/Auth.kt deleted file mode 100644 index d5c8761..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/model/Auth.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.domain.model - -data class LoginRequest(val username: String, val password: String) -data class RefreshRequest(val refresh: String) -data class LoginResponse(val access: String, val refresh: String) -data class RefreshResponse(val access: String) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/model/WorkOrder.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/model/WorkOrder.kt deleted file mode 100644 index d96620a..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/model/WorkOrder.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.domain.model -val wheelsCode = mapOf( - "DF" to 0b0001, - "PF" to 0b0010, - "DR" to 0b0100, - "PR" to 0b1000, - "DRO" to 0b00010000, - "PRO" to 0b100000 -) - -fun WorkOrder.computeWheelsNuts(): Int { - var wNut = 0 - var nutCount = 0 - val wheelSer = services?.get("0") - - wheelSer?.wheels?.forEach { wheelStr -> - if (wheelStr.isNotBlank()) { - wNut = wNut or (wheelsCode[wheelStr] ?: 0) - nutCount++ - } - } - return (wNut shl 8) or nutCount -} - -data class WorkOrder( - val id: String, - val status: String, - val car: String?, - val make: String?, - val licensePlate: String, - val services: Map?, - val measResult: Map?, - val measHistory: Map?, - val finishedAt: String?, - val createdAt: String, - val updatedAt: String? -) -data class Service( - val nut: Int?, - val status: Int, - val wheels: List?, - val wrench: String?, - val torqueRequired: Double -) diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/usecase/FetchWorkOrdersUseCase.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/usecase/FetchWorkOrdersUseCase.kt deleted file mode 100644 index 784759b..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/domain/usecase/FetchWorkOrdersUseCase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.domain.usecase - -import com.digitoolsolutions.app.torquevaultkmp.data.repository.WorkOrderRepository -import com.digitoolsolutions.app.torquevaultkmp.domain.model.WorkOrder - -class FetchWorkOrdersUseCase(private val repository: WorkOrderRepository) { - suspend operator fun invoke(page: Int): List { - return repository.fetchAbleWorkOrders(page) - } -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/MainScreen.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/MainScreen.kt index a7aefb9..b568228 100644 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/MainScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/MainScreen.kt @@ -28,9 +28,9 @@ import org.jetbrains.compose.resources.painterResource import org.koin.compose.viewmodel.koinViewModel @Composable fun MainScreen( + isLoggedIn: Boolean, isDarkTheme: Boolean, onThemeToggle: (Boolean) -> Unit, - isLoggedIn: Boolean, ) { val destinations = listOf( HomeDestination, @@ -77,7 +77,7 @@ fun MainScreen( ) { innerPadding -> NavHost( navController = navController, - startDestination = if (isLoggedIn) HomeDestination.route else AuthDestination.route, + startDestination = HomeDestination.route, modifier = Modifier.padding(innerPadding).fillMaxSize() ) { composable(AuthDestination.route) { LoginScreen(navController) } diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/auth/AuthViewModel.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/auth/AuthViewModel.kt deleted file mode 100644 index 5d9ca75..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/auth/AuthViewModel.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.screens.auth - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.digitoolsolutions.app.torquevaultkmp.data.network.api.AuthApi -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch - -sealed class LoginUiState { - object Idle : LoginUiState() - object Loading : LoginUiState() - object Success : LoginUiState() - data class Error(val message: String) : LoginUiState() -} - -class AuthViewModel( - private val authApi: AuthApi -) : ViewModel() { - - private val _loginState = MutableStateFlow(LoginUiState.Idle) - val loginState = _loginState.asStateFlow() - - fun login(username: String, password: String) { - viewModelScope.launch { - _loginState.value = LoginUiState.Loading - try { - val success = authApi.login(username, password) - if (success) { - _loginState.value = LoginUiState.Success - } else { - _loginState.value = LoginUiState.Error("Login failed: Invalid credentials") - } - } catch (e: Exception) { - _loginState.value = LoginUiState.Error("Error: ${e.message}") - } - } - } - - fun resetState() { - _loginState.value = LoginUiState.Idle - } -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/auth/LoginScreen.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/auth/LoginScreen.kt index c7070f1..e7bf1a7 100644 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/auth/LoginScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/auth/LoginScreen.kt @@ -54,7 +54,6 @@ import com.digitoolsolutions.app.torquevaultkmp.screens.home.HomeDestination import kotlinx.coroutines.launch import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource -import org.koin.compose.viewmodel.koinViewModel import torquevaultkmp.composeapp.generated.resources.Res import torquevaultkmp.composeapp.generated.resources.btn_login import torquevaultkmp.composeapp.generated.resources.dts_text @@ -65,17 +64,17 @@ import torquevaultkmp.composeapp.generated.resources.server_url_placeholder @Composable fun LoginScreen( - navController: NavController + navController: NavController, +// viewModel: AuthViewModel = hiltViewModel() ) { - val viewModel = koinViewModel() - val loginState by viewModel.loginState.collectAsState() +// val loginState by viewModel.loginState.collectAsState() LoginScreenContent( - viewModel = viewModel, - loginState = loginState, +// viewModel = viewModel, +// loginState = loginState, onLogin = { serverUrl, username, password -> // viewModel.updateServerUrl(serverUrl) - viewModel.login(username, password) +// viewModel.login(username, password) }, onLoginSuccess = { navController.navigate(HomeDestination.route) @@ -86,8 +85,8 @@ fun LoginScreen( @OptIn(ExperimentalMaterial3Api::class) @Composable fun LoginScreenContent( - viewModel: AuthViewModel, - loginState: LoginUiState, +// viewModel: AuthViewModel, +// loginState: LoginUiState, onLogin: (String, String, String) -> Unit, onLoginSuccess: () -> Unit ) { @@ -100,14 +99,14 @@ fun LoginScreenContent( val snackbarHostState = remember { SnackbarHostState() } val scope = rememberCoroutineScope() - LaunchedEffect(loginState) { - if (loginState is LoginUiState.Error) { - snackbarHostState.showSnackbar(loginState.message) - } - if (loginState is LoginUiState.Success) { - onLoginSuccess() - } - } +// LaunchedEffect(loginState) { +// if (loginState is LoginUiState.Error) { +// snackbarHostState.showSnackbar(loginState.message) +// } +// if (loginState is LoginUiState.Success) { +// onLoginSuccess() +// } +// } Scaffold( modifier = Modifier.imePadding(), snackbarHost = { SnackbarHost(snackbarHostState) }) @@ -204,18 +203,18 @@ fun LoginScreenContent( else onLogin(serverUrl.trim(), username.trim(), password.trim()) }, - enabled = loginState !is LoginUiState.Loading +// enabled = loginState !is LoginUiState.Loading ) { Text(stringResource(Res.string.btn_login)) - if (loginState is LoginUiState.Loading) { - Spacer(modifier = Modifier.width(16.dp)) - CircularProgressIndicator( - modifier = Modifier.size(20.dp), - color = Color.Blue, - strokeWidth = 2.dp - ) - - } +// if (loginState is LoginUiState.Loading) { +// Spacer(modifier = Modifier.width(16.dp)) +// CircularProgressIndicator( +// modifier = Modifier.size(20.dp), +// color = Color.Blue, +// strokeWidth = 2.dp +// ) +// +// } } } } @@ -225,8 +224,8 @@ fun LoginScreenContent( @Composable private fun LoginScreenPreview() { LoginScreenContent( - viewModel = viewModel(), - loginState = LoginUiState.Idle, +// viewModel = viewModel(), +// loginState = LoginUiState.Idle, onLogin = { _, _, _ -> }, onLoginSuccess = {} ) diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/home/HomeScreen.kt index d0cce46..3af0187 100644 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/home/HomeScreen.kt @@ -27,7 +27,6 @@ import androidx.compose.material3.SwipeToDismissBoxValue import androidx.compose.material3.Text import androidx.compose.material3.rememberSwipeToDismissBoxState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -42,14 +41,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.navigation.NavController import com.digitoolsolutions.app.torquevaultkmp.components.AppBar -import com.digitoolsolutions.app.torquevaultkmp.screens.auth.AuthDestination import com.digitoolsolutions.app.torquevaultkmp.screens.home.views.HomeEmpty import com.digitoolsolutions.app.torquevaultkmp.screens.home.views.HomeItem -import com.digitoolsolutions.app.torquevaultkmp.utils.ForceLogoutException -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.launch import org.jetbrains.compose.resources.stringResource -import org.koin.compose.viewmodel.koinViewModel import torquevaultkmp.composeapp.generated.resources.Res import torquevaultkmp.composeapp.generated.resources.home_title //import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -71,49 +65,31 @@ fun HomeScreen( ) { val snackbarHostState = remember { SnackbarHostState() } // val connectedDevice by bleScannerViewModel.connectedDevices.collectAsState(initial = emptyList()) - val viewModel = koinViewModel() - val workOrders by viewModel.workOrders.collectAsState() - LaunchedEffect(Unit) { - viewModel.loadWorkOrders() - } - - LaunchedEffect(viewModel) { - viewModel.error.collectLatest { error -> - if (error is ForceLogoutException) { - navController.navigate(AuthDestination.route) { - popUpTo(0) { inclusive = true } - } - } else { - snackbarHostState.showSnackbar(error.message ?: "Unknown error") - } - } - } - - Scaffold( + Scaffold ( modifier = Modifier.imePadding(), topBar = { AppBar( - title = { Text(stringResource(Res.string.home_title)) }, + title = {Text(stringResource(Res.string.home_title))}, ) }, snackbarHost = { SnackbarHost(snackbarHostState) }) { padding -> - Column( + Column ( modifier = Modifier .fillMaxSize() .imePadding() .padding(padding) .padding(horizontal = 16.dp, vertical = 12.dp), ) { - if (workOrders.isEmpty()) - HomeEmpty() - else - LazyColumn { - items(workOrders) { workOrder -> - HomeItem(workOrder) - } - } +// if(connectedDevice.isEmpty()) +// HomeEmpty() +// else +// LazyColumn { +// items(connectedDevice) { device -> +// HomeItem(device) +// } +// } } } } diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/home/HomeViewModel.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/home/HomeViewModel.kt deleted file mode 100644 index 0a879c3..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/screens/home/HomeViewModel.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.screens.home - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.digitoolsolutions.app.torquevaultkmp.domain.model.WorkOrder -import com.digitoolsolutions.app.torquevaultkmp.domain.usecase.FetchWorkOrdersUseCase -import com.digitoolsolutions.app.torquevaultkmp.utils.ForceLogoutException -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch - -class HomeViewModel( - private val fetchWorkOrdersUseCase: FetchWorkOrdersUseCase -): ViewModel() { - private val _workOrders = MutableStateFlow>(emptyList()) - val workOrders = _workOrders.asStateFlow() - - private val _error = MutableSharedFlow() - val error = _error.asSharedFlow() - - fun loadWorkOrders() { - viewModelScope.launch { - try { - val result = fetchWorkOrdersUseCase(page = 1) - _workOrders.value = result - } catch (e: Exception) { - _error.emit(e) - } - } - } -} diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/utils/ApiException.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/utils/ApiException.kt deleted file mode 100644 index 3418223..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/utils/ApiException.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.utils - -import io.ktor.http.HttpStatusCode - -class ApiException(val status: HttpStatusCode, val body: String) : Exception( - "API error ${status.value}: $body" -) - -class RetryWithNewTokenException : Exception(">>>>> Access token expired, retry with new token") -class ForceLogoutException : Exception(">>>>> Refresh token expired, force logout") - diff --git a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/utils/NoOpAntilog.kt b/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/utils/NoOpAntilog.kt deleted file mode 100644 index 5ecb272..0000000 --- a/composeApp/src/commonMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/utils/NoOpAntilog.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.utils - -import io.github.aakira.napier.Antilog -import io.github.aakira.napier.LogLevel - -class NoOpAntilog : Antilog() { - override fun isEnable(priority: LogLevel, tag: String?): Boolean = false - override fun performLog(priority: LogLevel, tag: String?, throwable: Throwable?, message: String?) {} -} \ No newline at end of file diff --git a/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppStorage.ios.kt deleted file mode 100644 index c5469ab..0000000 --- a/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/AppStorage.ios.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp - -import com.digitoolsolutions.app.torquevaultkmp.data.storage.AppStorage -import platform.Foundation.NSUserDefaults -import kotlin.reflect.KClass - -class AppStoragePlatform : AppStorage { - private val defaults = NSUserDefaults.standardUserDefaults() - - override fun save(key: String, value: T) { - when (value) { - is String -> defaults.setObject(value, forKey = key) - is Int -> defaults.setInteger(value.toLong(), forKey = key) - is Boolean -> defaults.setBool(value, forKey = key) - else -> throw IllegalArgumentException("Unsupported type") - } - } - - @Suppress("UNCHECKED_CAST") - override fun load(key: String, type: KClass): T? { - return when (type) { - String::class -> defaults.stringForKey(key) as T? - Int::class -> defaults.integerForKey(key).toInt() as T? - Boolean::class -> defaults.boolForKey(key) as T? - else -> throw IllegalArgumentException("Unsupported type") - } - } - override fun remove(key: String) { - defaults.removeObjectForKey(key) - } -} diff --git a/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MainViewController.kt b/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MainViewController.kt index 86e90fe..31facc2 100644 --- a/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MainViewController.kt +++ b/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/MainViewController.kt @@ -1,14 +1,5 @@ package com.digitoolsolutions.app.torquevaultkmp import androidx.compose.ui.window.ComposeUIViewController -import com.digitoolsolutions.app.torquevaultkmp.di.initKoin -import io.github.aakira.napier.DebugAntilog -import io.github.aakira.napier.Napier -fun MainViewController() = ComposeUIViewController( - configure = { - // Add logger lib - Napier.base(DebugAntilog()) // NoOpAntilog() for release - initKoin() - } -) { App() } \ No newline at end of file +fun MainViewController() = ComposeUIViewController { App() } \ No newline at end of file diff --git a/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.ios.kt b/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.ios.kt deleted file mode 100644 index 220eaaf..0000000 --- a/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/NetworkEngine.ios.kt +++ /dev/null @@ -1,76 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp - -import com.digitoolsolutions.app.torquevaultkmp.data.network.api.AuthApi -import com.digitoolsolutions.app.torquevaultkmp.data.storage.ReferKeys -import com.digitoolsolutions.app.torquevaultkmp.data.storage.TokenManager -import com.digitoolsolutions.app.torquevaultkmp.utils.ForceLogoutException -import io.github.aakira.napier.Napier -import io.ktor.client.HttpClient -import io.ktor.client.engine.darwin.Darwin -import io.ktor.client.plugins.DefaultRequest -import io.ktor.client.plugins.HttpSend -import io.ktor.client.plugins.HttpTimeout -import io.ktor.client.plugins.contentnegotiation.ContentNegotiation -import io.ktor.client.plugins.logging.DEFAULT -import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger -import io.ktor.client.plugins.logging.Logging -import io.ktor.client.plugins.plugin -import io.ktor.client.request.header -import io.ktor.http.HttpStatusCode -import io.ktor.serialization.kotlinx.json.json -import kotlinx.serialization.json.Json - -actual fun createPlatformHttpClient( - tokenManager: TokenManager, - authApi: AuthApi? -): HttpClient { - val client = HttpClient(Darwin) { - install(ContentNegotiation) { - json(Json { - ignoreUnknownKeys = true - isLenient = true - }) - } - install(DefaultRequest) { - tokenManager.getAccessToken()?.let { - header("Authorization", "Bearer $it") - } - } - install(HttpTimeout) { requestTimeoutMillis = 30_000 } - install(Logging) { - logger = object : Logger { - override fun log(message: String) { - Napier.v(message, tag = "HTTP") - } - } - level = LogLevel.ALL // set LogLevel.NONE for release - } - } - - if (authApi != null) { - client.plugin(HttpSend).intercept { request -> - // Skip interceptor for refresh token request to avoid recursion - if (request.url.pathSegments.contains(ReferKeys.REFRESH_TOKEN)) { - return@intercept execute(request) - } - - val originalCall = execute(request) - if (originalCall.response.status == HttpStatusCode.Unauthorized) { - println(">>>>> [NetworkEngine.ios.kt] Access token expired, auto request refresh token...") - val newToken = authApi.refreshToken() - if (newToken != null) { - request.headers["Authorization"] = "Bearer $newToken" - execute(request) - } else { - tokenManager.clearTokens() - throw ForceLogoutException() - } - } else { - originalCall - } - } - } - - return client -} diff --git a/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/PlatformModules.kt b/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/PlatformModules.kt deleted file mode 100644 index 9732439..0000000 --- a/composeApp/src/iosMain/kotlin/com/digitoolsolutions/app/torquevaultkmp/di/PlatformModules.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.digitoolsolutions.app.torquevaultkmp.di - -import com.digitoolsolutions.app.torquevaultkmp.AppStoragePlatform -import com.digitoolsolutions.app.torquevaultkmp.data.storage.AppStorage -import org.koin.dsl.module - -actual val platformModule = module { - single { AppStoragePlatform() } -} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 297314b..5ebee39 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,10 +14,11 @@ junit = "4.13.2" kotlin = "2.3.21" material3 = "1.10.0-alpha05" koin-bom = "4.2.1" -ktor = "3.4.3" material-icons-extended = "1.7.3" navigation-compose = "2.9.2" -logger = "2.7.1" +compose-multiplatform-adaptive = "1.3.0-alpha07" +compose-multiplatform-lifecycle = "2.10.0" + [libraries] kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } @@ -49,27 +50,13 @@ koin-compose = { module = "io.insert-koin:koin-compose" } koin-compose-viewmodel = { module = "io.insert-koin:koin-compose-viewmodel" } koin-ktor = { module = "io.insert-koin:koin-ktor" } koin-test = { module = "io.insert-koin:koin-test" } -ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } -ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } -ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" } -ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" } -ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } -ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" } -logger-napier = { module = "io.github.aakira:napier", version.ref = "logger" } + +#compose-material3-adaptiveNavigation3 = { module = "org.jetbrains.compose.material3.adaptive:adaptive-navigation3", version.ref = "compose-multiplatform-adaptive" } +#androidx-lifecycle-viewmodelNavigation3 = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "compose-multiplatform-lifecycle" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } androidLibrary = { id = "com.android.library", version.ref = "agp" } composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" } composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } -kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"} - -[bundles] -ktor = [ - "ktor-client-core", - "ktor-client-content-negotiation", -# "ktor-client-auth", - "ktor-client-logging", - "ktor-serialization-kotlinx-json" -] \ No newline at end of file +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } \ No newline at end of file diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index 95a2efb..11845e1 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -4,19 +4,5 @@ CADisableMinimumFrameDurationOnPhone - - NSAppTransportSecurity - - NSExceptionDomains - - digitoolsolutions.synology.me - - NSIncludesSubdomains - - NSTemporaryExceptionAllowsInsecureHTTPLoads - - - -