From 7c2b8e18eb0bf441612ebd4366e85c1d1c59f522 Mon Sep 17 00:00:00 2001 From: y9vad9 Date: Sun, 24 Dec 2023 01:21:38 +0100 Subject: [PATCH] chore: version bump & fixes --- ...ompose-multiplatform-convention.gradle.kts | 21 ++----- .../data/database/AccountDatabase.sq | 7 ++- .../data/AuthorizationsRepository.kt | 18 ++++-- .../data/DbAuthorizationMapper.kt | 2 + .../dependencies/AuthorizationDataModule.kt | 2 - .../screens/ConfirmAuthorizationModule.kt | 2 +- .../repositories/AuthorizationsRepository.kt | 2 +- .../ConfirmEmailAuthorizationUseCase.kt | 4 +- .../usecases/AuthorizeByEmailUseCaseTest.kt | 2 +- .../ConfirmEmailAuthorizationUseCaseTest.kt | 4 +- .../presentation/build.gradle.kts | 3 - .../ui/afterstart/AfterStartScreen.kt | 6 +- .../afterstart/mvi/AfterStartStateMachine.kt | 2 +- .../mvi/ConfirmAuthorizationMiddleware.kt | 2 +- .../InitialAuthorizationScreen.kt | 8 +-- .../mvi/InitialAuthorizationStateMachine.kt | 2 +- .../new_account_info/NewAccountInfoScreen.kt | 6 +- .../mvi/NewAccountInfoStateMachine.kt | 2 +- .../ui/start/mvi/StartAuthorizationReducer.kt | 2 +- .../ConfigureAccountMiddlewareTest.kt | 24 ++++---- .../ConfigureAccountReducerTest.kt | 55 +++++++++--------- feature/common/presentation/build.gradle.kts | 1 - .../AuthorizationFailureMiddlewareTest.kt | 2 +- .../timers/dependencies/TimersDataModule.kt | 2 +- .../screens/TimerSettingsModule.kt | 2 +- .../users/usecases/GetUserTimersUseCase.kt | 2 - .../timers/ui/settings/TimerSettingsScreen.kt | 3 +- .../settings/mvi/TimerSettingsMiddleware.kt | 2 +- .../ui/settings/mvi/TimerSettingsReducer.kt | 9 ++- .../ui/timer_creation/TimerCreationScreen.kt | 2 +- .../mvi/TimerCreationMiddleware.kt | 4 +- .../mvi/TimerCreationReducer.kt | 2 +- .../mvi/TimerCreationStateMachine.kt | 6 +- .../settings/TimerSettingsMiddlewareTest.kt | 1 - .../ui/settings/TimerSettingsReducerTest.kt | 2 +- .../app/users/data/database/CachedUsers.sq | 5 +- .../app/users/data/CachedUsersDataSource.kt | 14 ++--- .../app/users/data/UsersRepository.kt | 10 ++-- .../app/users/repositories/UsersRepository.kt | 2 - .../app/users/usecases/GetUserUseCase.kt | 2 +- .../app/users/usecases/EditUserUseCaseTest.kt | 3 +- .../app/users/usecases/GetUserUseCaseTest.kt | 39 +++++++------ .../app/users/usecases/GetUsersUseCaseTest.kt | 41 +++++++------ foundation/mvi/koin-compose/build.gradle.kts | 6 -- gradle.properties | 3 +- gradle/libs.versions.toml | 48 ++++++++------- platforms/android/build.gradle.kts | 7 +-- .../io/timemates/app/TimeMatesApplication.kt | 22 ++++--- platforms/desktop/build.gradle.kts | 4 +- .../kotlin/io/timemates/app/Dependencies.kt | 10 ++-- style-system/build.gradle.kts | 36 ++---------- .../app/style/system/appbar/AppBar.kt | 1 - .../app/style/system/images/CircleIcon.kt | 2 +- .../app/style/system/theme/AppFont.kt | 34 ----------- .../app/style/system/theme/AppTheme.kt | 11 ---- .../MR => libres}/fonts/Inter-Black.ttf | Bin .../MR => libres}/fonts/Inter-Bold.ttf | Bin .../MR => libres}/fonts/Inter-ExtraBold.ttf | Bin .../MR => libres}/fonts/Inter-ExtraLight.ttf | Bin .../MR => libres}/fonts/Inter-Light.ttf | Bin .../MR => libres}/fonts/Inter-Medium.ttf | Bin .../MR => libres}/fonts/Inter-Regular.ttf | Bin .../MR => libres}/fonts/Inter-SemiBold.ttf | Bin .../MR => libres}/fonts/Inter-Thin.ttf | Bin .../confirm_authorization_info_image.svg | 0 .../images/initial_screen_image.svg | 0 .../images/new_account_info_image.svg | 0 67 files changed, 218 insertions(+), 298 deletions(-) delete mode 100644 style-system/src/commonMain/kotlin/io/timemates/app/style/system/theme/AppFont.kt rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-Black.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-Bold.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-ExtraBold.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-ExtraLight.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-Light.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-Medium.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-Regular.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-SemiBold.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/fonts/Inter-Thin.ttf (100%) rename style-system/src/commonMain/{resources/MR => libres}/images/confirm_authorization_info_image.svg (100%) rename style-system/src/commonMain/{resources/MR => libres}/images/initial_screen_image.svg (100%) rename style-system/src/commonMain/{resources/MR => libres}/images/new_account_info_image.svg (100%) diff --git a/build-plugins/configurations/src/main/kotlin/compose-multiplatform-convention.gradle.kts b/build-plugins/configurations/src/main/kotlin/compose-multiplatform-convention.gradle.kts index fd42274..c931e68 100644 --- a/build-plugins/configurations/src/main/kotlin/compose-multiplatform-convention.gradle.kts +++ b/build-plugins/configurations/src/main/kotlin/compose-multiplatform-convention.gradle.kts @@ -1,32 +1,23 @@ plugins { id("multiplatform-library-convention") id("org.jetbrains.compose") - id("com.android.library") -} - -kotlin { - jvmToolchain(19) } android { - compileSdk = 34 - defaultConfig { namespace = "io.timemates.app.ui" } - compileOptions { - targetCompatibility = JavaVersion.VERSION_19 - sourceCompatibility = JavaVersion.VERSION_19 + buildFeatures { + compose = true } } dependencies { - commonMainImplementation(compose.ui) - commonMainImplementation(compose.foundation) + commonMainApi(compose.ui) + commonMainApi(compose.foundation) commonMainImplementation(compose.runtime) - commonMainImplementation(compose.material3) - commonMainImplementation(compose.materialIconsExtended) - commonMainImplementation(compose.uiTooling) + commonMainApi(compose.material3) + commonMainApi(compose.materialIconsExtended) } diff --git a/feature/authorization/data/database/src/commonMain/sqldelight/io/timemates/app/authorization/data/database/AccountDatabase.sq b/feature/authorization/data/database/src/commonMain/sqldelight/io/timemates/app/authorization/data/database/AccountDatabase.sq index cdefe93..c230206 100644 --- a/feature/authorization/data/database/src/commonMain/sqldelight/io/timemates/app/authorization/data/database/AccountDatabase.sq +++ b/feature/authorization/data/database/src/commonMain/sqldelight/io/timemates/app/authorization/data/database/AccountDatabase.sq @@ -6,7 +6,8 @@ CREATE TABLE Authorization ( refreshHashExpiresAt INTEGER NOT NULL, generationTime INTEGER NOT NULL, metadataClientName TEXT, - metadataClientVersion TEXT, + metadataClientVersion REAL NOT NULL, + userId INTEGER NOT NULL, metadataClientIpAddress TEXT, isCurrent INTEGER AS kotlin.Boolean NOT NULL ); @@ -43,5 +44,5 @@ FROM Authorization WHERE isCurrent; add: -INSERT INTO Authorization (id, accessHashValue, accessHashExpiresAt, refreshHashValue, refreshHashExpiresAt, generationTime, metadataClientName, metadataClientVersion, metadataClientIpAddress, isCurrent) -VALUES (:id, :accessHashValue, :accessHashExpiresAt, :refreshHashValue, :refreshHashExpiresAt, :generationTime, :metadataClientName, :metadataClientVersion, :metadataClientIpAddress, TRUE); +INSERT INTO Authorization (id, accessHashValue, accessHashExpiresAt, refreshHashValue, refreshHashExpiresAt, generationTime, metadataClientName, metadataClientVersion, metadataClientIpAddress, userId, isCurrent) +VALUES (:id, :accessHashValue, :accessHashExpiresAt, :refreshHashValue, :refreshHashExpiresAt, :generationTime, :metadataClientName, :metadataClientVersion, :metadataClientIpAddress, :userId, TRUE); diff --git a/feature/authorization/data/src/commonMain/kotlin/io/timemates/app/authorization/data/AuthorizationsRepository.kt b/feature/authorization/data/src/commonMain/kotlin/io/timemates/app/authorization/data/AuthorizationsRepository.kt index d2b18e4..a57f97f 100644 --- a/feature/authorization/data/src/commonMain/kotlin/io/timemates/app/authorization/data/AuthorizationsRepository.kt +++ b/feature/authorization/data/src/commonMain/kotlin/io/timemates/app/authorization/data/AuthorizationsRepository.kt @@ -7,7 +7,11 @@ import io.timemates.sdk.authorization.email.requests.ConfirmAuthorizationRequest import io.timemates.sdk.authorization.email.types.value.VerificationHash import io.timemates.sdk.authorization.sessions.AuthorizedSessionsApi import io.timemates.sdk.authorization.sessions.types.Authorization +import io.timemates.sdk.authorization.sessions.types.value.ApplicationName +import io.timemates.sdk.authorization.sessions.types.value.ClientIpAddress +import io.timemates.sdk.authorization.sessions.types.value.ClientVersion import io.timemates.sdk.authorization.sessions.types.value.ConfirmationCode +import io.timemates.sdk.common.constructor.createOrThrow import io.timemates.sdk.common.exceptions.UnsupportedException import io.timemates.sdk.users.profile.types.value.EmailAddress import io.timemates.sdk.users.profile.types.value.UserDescription @@ -31,10 +35,10 @@ class AuthorizationsRepository( } override suspend fun authorize(emailAddress: EmailAddress): Result { - return emailAuthApi.authorize(emailAddress) + return emailAuthApi.authorize(emailAddress, Authorization.Metadata(appName, appVersion, ClientIpAddress.createOrThrow("UNDEFINED"))) } - override suspend fun confirm(verificationHash: VerificationHash, code: ConfirmationCode): Result { + override suspend fun confirm(verificationHash: VerificationHash, code: ConfirmationCode): Result { return emailAuthApi.confirm(verificationHash, code) } @@ -53,9 +57,10 @@ class AuthorizationsRepository( refreshHashValue = refreshHash!!.value.string, refreshHashExpiresAt = refreshHash!!.expiresAt.toEpochMilliseconds(), generationTime = generationTime.toEpochMilliseconds(), - metadataClientName = metadata?.clientName?.string, + metadataClientName = metadata?.applicationName?.string, metadataClientIpAddress = metadata?.clientIpAddress?.string, - metadataClientVersion = metadata?.clientVersion?.string + metadataClientVersion = metadata?.clientVersion!!.double, + userId = userId.long, ) } } @@ -63,4 +68,7 @@ class AuthorizationsRepository( } -} \ No newline at end of file +} + +private val appName = ApplicationName.createOrThrow("TimeMates App") +private val appVersion = ClientVersion.createOrThrow(1.0) \ No newline at end of file diff --git a/feature/authorization/data/src/commonMain/kotlin/io/timemates/app/authorization/data/DbAuthorizationMapper.kt b/feature/authorization/data/src/commonMain/kotlin/io/timemates/app/authorization/data/DbAuthorizationMapper.kt index 86cb38d..0297b40 100644 --- a/feature/authorization/data/src/commonMain/kotlin/io/timemates/app/authorization/data/DbAuthorizationMapper.kt +++ b/feature/authorization/data/src/commonMain/kotlin/io/timemates/app/authorization/data/DbAuthorizationMapper.kt @@ -3,6 +3,7 @@ package io.timemates.app.authorization.data import io.timemates.sdk.authorization.sessions.types.Authorization import io.timemates.sdk.authorization.types.value.HashValue import io.timemates.sdk.common.constructor.createOrThrow +import io.timemates.sdk.users.profile.types.value.UserId import kotlinx.datetime.Instant import io.timemates.app.authorization.data.database.Authorization as DbAuthorization @@ -18,6 +19,7 @@ class DbAuthorizationMapper { Instant.fromEpochMilliseconds(accessHashExpiresAt), ), generationTime = Instant.fromEpochMilliseconds(generationTime), + userId = UserId.createOrThrow(userId), metadata = null, // TODO when metadata will be implemented on server ) } diff --git a/feature/authorization/dependencies/src/commonMain/kotlin/io/timemates/app/authorization/dependencies/AuthorizationDataModule.kt b/feature/authorization/dependencies/src/commonMain/kotlin/io/timemates/app/authorization/dependencies/AuthorizationDataModule.kt index 46d1d68..5a6d6a7 100644 --- a/feature/authorization/dependencies/src/commonMain/kotlin/io/timemates/app/authorization/dependencies/AuthorizationDataModule.kt +++ b/feature/authorization/dependencies/src/commonMain/kotlin/io/timemates/app/authorization/dependencies/AuthorizationDataModule.kt @@ -3,7 +3,6 @@ package io.timemates.app.authorization.dependencies import app.cash.sqldelight.db.SqlDriver import io.timemates.app.authorization.data.DatabaseAccessHashProvider import io.timemates.app.authorization.data.DbAuthorizationMapper -import io.timemates.app.authorization.data.database.AccountDatabaseQueries import io.timemates.app.authorization.repositories.AuthorizationsRepository import io.timemates.data.database.TimeMatesAuthorizations import io.timemates.sdk.authorization.email.EmailAuthorizationApi @@ -13,7 +12,6 @@ import io.timemates.sdk.common.providers.AccessHashProvider import org.koin.core.annotation.Factory import org.koin.core.annotation.Module import org.koin.core.annotation.Named -import org.koin.core.annotation.Singleton import io.timemates.app.authorization.data.AuthorizationsRepository as AuthorizationsRepositoryImpl @Module diff --git a/feature/authorization/dependencies/src/commonMain/kotlin/io/timemates/app/authorization/dependencies/screens/ConfirmAuthorizationModule.kt b/feature/authorization/dependencies/src/commonMain/kotlin/io/timemates/app/authorization/dependencies/screens/ConfirmAuthorizationModule.kt index f504531..361e636 100644 --- a/feature/authorization/dependencies/src/commonMain/kotlin/io/timemates/app/authorization/dependencies/screens/ConfirmAuthorizationModule.kt +++ b/feature/authorization/dependencies/src/commonMain/kotlin/io/timemates/app/authorization/dependencies/screens/ConfirmAuthorizationModule.kt @@ -25,7 +25,7 @@ class ConfirmAuthorizationModule { @Factory fun confirmAuthorizationUseCase( - authorizationsRepository: AuthorizationsRepository + authorizationsRepository: AuthorizationsRepository, ): ConfirmEmailAuthorizationUseCase = ConfirmEmailAuthorizationUseCase(authorizationsRepository) @Factory diff --git a/feature/authorization/domain/src/commonMain/kotlin/io/timemates/app/authorization/repositories/AuthorizationsRepository.kt b/feature/authorization/domain/src/commonMain/kotlin/io/timemates/app/authorization/repositories/AuthorizationsRepository.kt index 3224cd5..3694f4e 100644 --- a/feature/authorization/domain/src/commonMain/kotlin/io/timemates/app/authorization/repositories/AuthorizationsRepository.kt +++ b/feature/authorization/domain/src/commonMain/kotlin/io/timemates/app/authorization/repositories/AuthorizationsRepository.kt @@ -45,7 +45,7 @@ interface AuthorizationsRepository { * @return A [Result] indicating the success or failure of the confirmation request, * with the response from the confirmation as the successful result. */ - suspend fun confirm(verificationHash: VerificationHash, code: ConfirmationCode): Result + suspend fun confirm(verificationHash: VerificationHash, code: ConfirmationCode): Result /** * Creates a new user account with the given verification hash, user name, and user description. diff --git a/feature/authorization/domain/src/commonMain/kotlin/io/timemates/app/authorization/usecases/ConfirmEmailAuthorizationUseCase.kt b/feature/authorization/domain/src/commonMain/kotlin/io/timemates/app/authorization/usecases/ConfirmEmailAuthorizationUseCase.kt index 356321b..de24e5c 100644 --- a/feature/authorization/domain/src/commonMain/kotlin/io/timemates/app/authorization/usecases/ConfirmEmailAuthorizationUseCase.kt +++ b/feature/authorization/domain/src/commonMain/kotlin/io/timemates/app/authorization/usecases/ConfirmEmailAuthorizationUseCase.kt @@ -8,11 +8,11 @@ import io.timemates.sdk.common.exceptions.InvalidArgumentException import io.timemates.sdk.common.exceptions.TooManyRequestsException class ConfirmEmailAuthorizationUseCase( - private val authorizations: AuthorizationsRepository + private val authorizations: AuthorizationsRepository, ) { suspend fun execute( verificationHash: VerificationHash, - code: ConfirmationCode + code: ConfirmationCode, ): Result { return authorizations.confirm(verificationHash, code) .map { result -> Result.Success(result.authorization, result.isNewAccount) } diff --git a/feature/authorization/domain/src/jvmTest/kotlin/io/timemates/app/authorization/usecases/AuthorizeByEmailUseCaseTest.kt b/feature/authorization/domain/src/jvmTest/kotlin/io/timemates/app/authorization/usecases/AuthorizeByEmailUseCaseTest.kt index 796ea60..4b0d32b 100644 --- a/feature/authorization/domain/src/jvmTest/kotlin/io/timemates/app/authorization/usecases/AuthorizeByEmailUseCaseTest.kt +++ b/feature/authorization/domain/src/jvmTest/kotlin/io/timemates/app/authorization/usecases/AuthorizeByEmailUseCaseTest.kt @@ -39,7 +39,7 @@ class AuthorizeByEmailUseCaseTest { fun `execute with TooManyRequestsException should return TooManyRequests`() { // GIVEN val emailAddress = EmailAddress.createOrThrow("test@example.com") - val exception = TooManyRequestsException("Too many requests") + val exception = TooManyRequestsException("Too many requests", cause = null) coEvery { authorizationsRepository.authorize(emailAddress) } returns Result.failure(exception) // WHEN diff --git a/feature/authorization/domain/src/jvmTest/kotlin/io/timemates/app/authorization/usecases/ConfirmEmailAuthorizationUseCaseTest.kt b/feature/authorization/domain/src/jvmTest/kotlin/io/timemates/app/authorization/usecases/ConfirmEmailAuthorizationUseCaseTest.kt index de3d31a..90f0d21 100644 --- a/feature/authorization/domain/src/jvmTest/kotlin/io/timemates/app/authorization/usecases/ConfirmEmailAuthorizationUseCaseTest.kt +++ b/feature/authorization/domain/src/jvmTest/kotlin/io/timemates/app/authorization/usecases/ConfirmEmailAuthorizationUseCaseTest.kt @@ -30,7 +30,7 @@ class ConfirmEmailAuthorizationUseCaseTest { val isNewAccount = false coEvery { authorizationsRepository.confirm(verificationHash, confirmationCode) } returns Result.success( - ConfirmAuthorizationRequest.Response( + ConfirmAuthorizationRequest.Result( isNewAccount, authorization, ) ) @@ -47,7 +47,7 @@ class ConfirmEmailAuthorizationUseCaseTest { // GIVEN val verificationHash = VerificationHash.createOrThrow(Random.nextString(VerificationHash.SIZE)) val confirmationCode = ConfirmationCode.createOrThrow("123456") - val exception = TooManyRequestsException("Too many requests") + val exception = TooManyRequestsException("Too many requests", cause = null) coEvery { authorizationsRepository.confirm(verificationHash, confirmationCode) } returns Result.failure(exception) // WHEN diff --git a/feature/authorization/presentation/build.gradle.kts b/feature/authorization/presentation/build.gradle.kts index 5de3879..b1fdbe0 100644 --- a/feature/authorization/presentation/build.gradle.kts +++ b/feature/authorization/presentation/build.gradle.kts @@ -12,8 +12,5 @@ dependencies { commonMainImplementation(projects.styleSystem) commonTestImplementation(projects.foundation.random) - - commonMainImplementation(libs.moko.resources.compose) - commonMainImplementation(libs.moko.resources) } diff --git a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/afterstart/AfterStartScreen.kt b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/afterstart/AfterStartScreen.kt index 6d56027..f1ae895 100644 --- a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/afterstart/AfterStartScreen.kt +++ b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/afterstart/AfterStartScreen.kt @@ -21,8 +21,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import dev.icerock.moko.resources.compose.fontFamilyResource -import dev.icerock.moko.resources.compose.painterResource +import io.github.skeptick.libres.compose.painterResource import io.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine import io.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine.Event import io.timemates.app.foundation.mvi.EmptyState @@ -40,7 +39,7 @@ fun AfterStartScreen( navigateToConfirmation: (String) -> Unit, navigateToStart: () -> Unit, ) { - val painter: Painter = painterResource(Resources.images.confirm_authorization_info_image) + val painter: Painter = Resources.image.confirm_authorization_info_image.painterResource() LaunchedEffect(Unit) { stateMachine.effects.consumeEach { effect -> @@ -90,7 +89,6 @@ fun AfterStartScreen( Text( text = LocalStrings.current.confirmation, modifier = Modifier, - fontFamily = fontFamilyResource(Resources.fonts.Inter.black), style = MaterialTheme.typography.titleLarge, ) diff --git a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/afterstart/mvi/AfterStartStateMachine.kt b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/afterstart/mvi/AfterStartStateMachine.kt index 340c3a3..e198b57 100644 --- a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/afterstart/mvi/AfterStartStateMachine.kt +++ b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/afterstart/mvi/AfterStartStateMachine.kt @@ -2,8 +2,8 @@ package io.timemates.app.authorization.ui.afterstart.mvi import io.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine.Effect import io.timemates.app.authorization.ui.afterstart.mvi.AfterStartStateMachine.Event -import io.timemates.app.foundation.mvi.StateMachine import io.timemates.app.foundation.mvi.EmptyState +import io.timemates.app.foundation.mvi.StateMachine import io.timemates.app.foundation.mvi.UiEffect import io.timemates.app.foundation.mvi.UiEvent import io.timemates.sdk.authorization.email.types.value.VerificationHash diff --git a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/confirmation/mvi/ConfirmAuthorizationMiddleware.kt b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/confirmation/mvi/ConfirmAuthorizationMiddleware.kt index 8f424e1..8b2969a 100644 --- a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/confirmation/mvi/ConfirmAuthorizationMiddleware.kt +++ b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/confirmation/mvi/ConfirmAuthorizationMiddleware.kt @@ -17,7 +17,7 @@ class ConfirmAuthorizationMiddleware : Middleware { Effect.TooManyAttempts, Effect.AttemptIsFailed, is Effect.NavigateToCreateAccount, - is Effect.NavigateToHome + is Effect.NavigateToHome, -> store.state.value.copy(isLoading = false) } } diff --git a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/initial_authorization/InitialAuthorizationScreen.kt b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/initial_authorization/InitialAuthorizationScreen.kt index 754c328..976faa7 100644 --- a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/initial_authorization/InitialAuthorizationScreen.kt +++ b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/initial_authorization/InitialAuthorizationScreen.kt @@ -18,8 +18,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import dev.icerock.moko.resources.compose.fontFamilyResource -import dev.icerock.moko.resources.compose.painterResource +import io.github.skeptick.libres.compose.painterResource import io.timemates.app.authorization.ui.initial_authorization.mvi.InitialAuthorizationStateMachine.Effect import io.timemates.app.authorization.ui.initial_authorization.mvi.InitialAuthorizationStateMachine.Event import io.timemates.app.foundation.mvi.EmptyState @@ -35,10 +34,10 @@ fun InitialAuthorizationScreen( stateMachine: StateMachine, navigateToStartAuthorization: () -> Unit, ) { - val painter: Painter = painterResource(Resources.images.initial_screen_image) + val painter: Painter = Resources.image.initial_screen_image.painterResource() LaunchedEffect(Unit) { - stateMachine.effects.consumeEach { effect -> + stateMachine.effects.consumeEach { effect -> when (effect) { is Effect.NavigateToStart -> navigateToStartAuthorization() @@ -68,7 +67,6 @@ fun InitialAuthorizationScreen( Text( text = LocalStrings.current.welcome, modifier = Modifier, - fontFamily = fontFamilyResource(Resources.fonts.Inter.black), textAlign = TextAlign.Center, style = MaterialTheme.typography.titleLarge, ) diff --git a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/initial_authorization/mvi/InitialAuthorizationStateMachine.kt b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/initial_authorization/mvi/InitialAuthorizationStateMachine.kt index 5d36eed..47f4f43 100644 --- a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/initial_authorization/mvi/InitialAuthorizationStateMachine.kt +++ b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/initial_authorization/mvi/InitialAuthorizationStateMachine.kt @@ -9,7 +9,7 @@ import io.timemates.app.foundation.mvi.UiEvent class InitialAuthorizationStateMachine( reducer: InitialAuthorizationReducer, -): StateMachine( +) : StateMachine( reducer = reducer, middlewares = emptyList(), ) { diff --git a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/new_account_info/NewAccountInfoScreen.kt b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/new_account_info/NewAccountInfoScreen.kt index 41189b7..5421a9b 100644 --- a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/new_account_info/NewAccountInfoScreen.kt +++ b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/new_account_info/NewAccountInfoScreen.kt @@ -21,8 +21,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import dev.icerock.moko.resources.compose.fontFamilyResource -import dev.icerock.moko.resources.compose.painterResource +import io.github.skeptick.libres.compose.painterResource import io.timemates.app.authorization.ui.new_account_info.mvi.NewAccountInfoStateMachine.Effect import io.timemates.app.authorization.ui.new_account_info.mvi.NewAccountInfoStateMachine.Event import io.timemates.app.foundation.mvi.EmptyState @@ -40,7 +39,7 @@ fun NewAccountInfoScreen( navigateToConfigure: (String) -> Unit, navigateToStart: () -> Unit, ) { - val painter: Painter = painterResource(Resources.images.new_account_info_image) + val painter: Painter = Resources.image.new_account_info_image.painterResource() LaunchedEffect(Unit) { stateMachine.effects.consumeEach { effect -> @@ -90,7 +89,6 @@ fun NewAccountInfoScreen( Text( text = LocalStrings.current.almostDone, modifier = Modifier, - fontFamily = fontFamilyResource(Resources.fonts.Inter.black), style = MaterialTheme.typography.titleLarge, ) diff --git a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/new_account_info/mvi/NewAccountInfoStateMachine.kt b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/new_account_info/mvi/NewAccountInfoStateMachine.kt index c9a8ae8..6bce6c9 100644 --- a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/new_account_info/mvi/NewAccountInfoStateMachine.kt +++ b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/new_account_info/mvi/NewAccountInfoStateMachine.kt @@ -2,8 +2,8 @@ package io.timemates.app.authorization.ui.new_account_info.mvi import io.timemates.app.authorization.ui.new_account_info.mvi.NewAccountInfoStateMachine.Effect import io.timemates.app.authorization.ui.new_account_info.mvi.NewAccountInfoStateMachine.Event -import io.timemates.app.foundation.mvi.StateMachine import io.timemates.app.foundation.mvi.EmptyState +import io.timemates.app.foundation.mvi.StateMachine import io.timemates.app.foundation.mvi.UiEffect import io.timemates.app.foundation.mvi.UiEvent import io.timemates.sdk.authorization.email.types.value.VerificationHash diff --git a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/start/mvi/StartAuthorizationReducer.kt b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/start/mvi/StartAuthorizationReducer.kt index 729586b..5999773 100644 --- a/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/start/mvi/StartAuthorizationReducer.kt +++ b/feature/authorization/presentation/src/commonMain/kotlin/io/timemates/app/authorization/ui/start/mvi/StartAuthorizationReducer.kt @@ -41,7 +41,7 @@ class StartAuthorizationReducer( private fun authorizeWithEmail( email: String, - sendEffect: (Effect) -> Unit + sendEffect: (Effect) -> Unit, ) { coroutineScope.launch { when (val result = authorizeByEmail.execute(EmailAddress.createOrThrow(email))) { diff --git a/feature/authorization/presentation/src/jvmTest/kotlin/io/timemates/app/authorization/ui/configure_account/ConfigureAccountMiddlewareTest.kt b/feature/authorization/presentation/src/jvmTest/kotlin/io/timemates/app/authorization/ui/configure_account/ConfigureAccountMiddlewareTest.kt index ae47de7..172b3dd 100644 --- a/feature/authorization/presentation/src/jvmTest/kotlin/io/timemates/app/authorization/ui/configure_account/ConfigureAccountMiddlewareTest.kt +++ b/feature/authorization/presentation/src/jvmTest/kotlin/io/timemates/app/authorization/ui/configure_account/ConfigureAccountMiddlewareTest.kt @@ -22,30 +22,30 @@ class ConfigureAccountMiddlewareTest { // WHEN effects.map { effect -> effect to middleware.onEffect(effect, stateStore) } - // THEN - .forEach { (effect, state) -> - assert(!state.isLoading) { - "${effect::class.simpleName} effect does not change loading status to false." - } + // THEN + .forEach { (effect, state) -> + assert(!state.isLoading) { + "${effect::class.simpleName} effect does not change loading status to false." } + } } @Test fun `effects not produced by network operations should not remove loading status`() { // GIVEN val effects = listOf( - ConfigureAccountStateMachine.Effect.NavigateToStart, - ConfigureAccountStateMachine.Effect.NavigateToHomePage(authorization) + ConfigureAccountStateMachine.Effect.NavigateToStart, + ConfigureAccountStateMachine.Effect.NavigateToHomePage(authorization) ) every { stateStore.state } returns MutableStateFlow(ConfigureAccountStateMachine.State(isLoading = true)) // WHEN effects.map { effect -> effect to middleware.onEffect(effect, stateStore) } - // THEN - .forEach { (effect, state) -> - assert(state.isLoading) { - "${effect::class.simpleName} effect changes loading status regardless it shouldn't" - } + // THEN + .forEach { (effect, state) -> + assert(state.isLoading) { + "${effect::class.simpleName} effect changes loading status regardless it shouldn't" } + } } } diff --git a/feature/authorization/presentation/src/jvmTest/kotlin/io/timemates/app/authorization/ui/configure_account/ConfigureAccountReducerTest.kt b/feature/authorization/presentation/src/jvmTest/kotlin/io/timemates/app/authorization/ui/configure_account/ConfigureAccountReducerTest.kt index cf16ae7..297f28e 100644 --- a/feature/authorization/presentation/src/jvmTest/kotlin/io/timemates/app/authorization/ui/configure_account/ConfigureAccountReducerTest.kt +++ b/feature/authorization/presentation/src/jvmTest/kotlin/io/timemates/app/authorization/ui/configure_account/ConfigureAccountReducerTest.kt @@ -3,16 +3,13 @@ package io.timemates.app.authorization.ui.configure_account import io.mockk.every import io.mockk.mockk import io.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountReducer -import io.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountStateMachine -import io.timemates.app.authorization.ui.start.mvi.StartAuthorizationStateMachine +import io.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountStateMachine.Event +import io.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountStateMachine.State import io.timemates.app.authorization.usecases.CreateNewAccountUseCase import io.timemates.app.authorization.validation.UserDescriptionValidator import io.timemates.app.authorization.validation.UserNameValidator import io.timemates.sdk.authorization.email.types.value.VerificationHash import kotlinx.coroutines.test.TestScope -import io.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountStateMachine.Effect -import io.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountStateMachine.Event -import io.timemates.app.authorization.ui.configure_account.mvi.ConfigureAccountStateMachine.State import org.junit.jupiter.api.Test import kotlin.test.assertEquals @@ -39,23 +36,23 @@ class ConfigureAccountReducerTest { fun `OnDoneClicked with valid name should set appropriate state`() { // GIVEN every { userNameValidator.validate(any()) } returns - UserNameValidator.Result.Success + UserNameValidator.Result.Success every { userDescriptionValidator.validate(any()) } returns - UserDescriptionValidator.Result.Success + UserDescriptionValidator.Result.Success // WHEN val result = reducer.reduce(State(name = validName, aboutYou = validDescription), Event.OnDoneClicked) {} // THEN assertEquals( - expected = State( - name = validName, - aboutYou = validDescription, - isNameSizeInvalid = false, - isAboutYouSizeInvalid = false, - isLoading = true - ), - actual = result + expected = State( + name = validName, + aboutYou = validDescription, + isNameSizeInvalid = false, + isAboutYouSizeInvalid = false, + isLoading = true + ), + actual = result ) } @@ -63,23 +60,23 @@ class ConfigureAccountReducerTest { fun `OnDoneClicked with valid description should set appropriate state`() { // GIVEN every { userNameValidator.validate(any()) } returns - UserNameValidator.Result.Success + UserNameValidator.Result.Success every { userDescriptionValidator.validate(any()) } returns - UserDescriptionValidator.Result.Success + UserDescriptionValidator.Result.Success // WHEN val result = reducer.reduce(State(name = validName, aboutYou = validDescription), Event.OnDoneClicked) {} // THEN assertEquals( - expected = State( - name = validName, - aboutYou = validDescription, - isNameSizeInvalid = false, - isAboutYouSizeInvalid = false, - isLoading = true - ), - actual = result + expected = State( + name = validName, + aboutYou = validDescription, + isNameSizeInvalid = false, + isAboutYouSizeInvalid = false, + isLoading = true + ), + actual = result ) } @@ -87,9 +84,9 @@ class ConfigureAccountReducerTest { fun `OnDoneClicked with invalid name should set appropriate state`() { // GIVEN every { userNameValidator.validate(any()) } returns - UserNameValidator.Result.SizeViolation + UserNameValidator.Result.SizeViolation every { userDescriptionValidator.validate(any()) } returns - UserDescriptionValidator.Result.Success + UserDescriptionValidator.Result.Success // WHEN val result = reducer.reduce(State(name = invalidName, aboutYou = validDescription), Event.OnDoneClicked) {} @@ -111,9 +108,9 @@ class ConfigureAccountReducerTest { fun `OnDoneClicked with invalid description should set appropriate state`() { // GIVEN every { userNameValidator.validate(any()) } returns - UserNameValidator.Result.Success + UserNameValidator.Result.Success every { userDescriptionValidator.validate(any()) } returns - UserDescriptionValidator.Result.SizeViolation + UserDescriptionValidator.Result.SizeViolation // WHEN val result = reducer.reduce(State(name = validName, aboutYou = invalidDescription), Event.OnDoneClicked) {} diff --git a/feature/common/presentation/build.gradle.kts b/feature/common/presentation/build.gradle.kts index 1be1da2..5e7e64f 100644 --- a/feature/common/presentation/build.gradle.kts +++ b/feature/common/presentation/build.gradle.kts @@ -13,7 +13,6 @@ dependencies { commonMainImplementation(libs.kotlinx.coroutines) commonMainApi(compose.materialIconsExtended) - commonMainApi(compose.uiTooling) commonMainApi(libs.koin.core) diff --git a/feature/common/presentation/src/jvmTest/kotlin/io/timemates/app/common/mvi/middleware/AuthorizationFailureMiddlewareTest.kt b/feature/common/presentation/src/jvmTest/kotlin/io/timemates/app/common/mvi/middleware/AuthorizationFailureMiddlewareTest.kt index f20d5f0..665cf01 100644 --- a/feature/common/presentation/src/jvmTest/kotlin/io/timemates/app/common/mvi/middleware/AuthorizationFailureMiddlewareTest.kt +++ b/feature/common/presentation/src/jvmTest/kotlin/io/timemates/app/common/mvi/middleware/AuthorizationFailureMiddlewareTest.kt @@ -23,7 +23,7 @@ class AuthorizationFailureMiddlewareTest { private sealed class TestEffect : UiEffect { data class AuthorizationFailure( - override val exception: UnauthorizedException + override val exception: UnauthorizedException, ) : TestEffect(), AuthorizationFailureEffect data object AnyOther : TestEffect() diff --git a/feature/timers/dependencies/src/commonMain/kotlin/io/timemates/app/timers/dependencies/TimersDataModule.kt b/feature/timers/dependencies/src/commonMain/kotlin/io/timemates/app/timers/dependencies/TimersDataModule.kt index fd1c72b..4030cdf 100644 --- a/feature/timers/dependencies/src/commonMain/kotlin/io/timemates/app/timers/dependencies/TimersDataModule.kt +++ b/feature/timers/dependencies/src/commonMain/kotlin/io/timemates/app/timers/dependencies/TimersDataModule.kt @@ -19,7 +19,7 @@ class TimersDataModule { @Factory fun timersRepository( timersApi: TimersApi, - ): TimersRepository = TimersRepositoryImpl ( + ): TimersRepository = TimersRepositoryImpl( timersApi, timersApi.members.invites, timersApi.sessions, timersApi.members, ) } \ No newline at end of file diff --git a/feature/timers/dependencies/src/commonMain/kotlin/io/timemates/app/timers/dependencies/screens/TimerSettingsModule.kt b/feature/timers/dependencies/src/commonMain/kotlin/io/timemates/app/timers/dependencies/screens/TimerSettingsModule.kt index 43af471..f841d8a 100644 --- a/feature/timers/dependencies/src/commonMain/kotlin/io/timemates/app/timers/dependencies/screens/TimerSettingsModule.kt +++ b/feature/timers/dependencies/src/commonMain/kotlin/io/timemates/app/timers/dependencies/screens/TimerSettingsModule.kt @@ -39,7 +39,7 @@ class TimerSettingsModule { timerNameValidator: TimerNameValidator, timerDescriptionValidator: TimerDescriptionValidator, middleware: TimerSettingsMiddleware, - ): TimerSettingsStateMachine { + ): TimerSettingsStateMachine { return TimerSettingsStateMachine( reducer = TimerSettingsReducer( timerId = timerId, diff --git a/feature/timers/domain/src/commonMain/kotlin/io/timemates/app/users/usecases/GetUserTimersUseCase.kt b/feature/timers/domain/src/commonMain/kotlin/io/timemates/app/users/usecases/GetUserTimersUseCase.kt index e560b7a..340c4f4 100644 --- a/feature/timers/domain/src/commonMain/kotlin/io/timemates/app/users/usecases/GetUserTimersUseCase.kt +++ b/feature/timers/domain/src/commonMain/kotlin/io/timemates/app/users/usecases/GetUserTimersUseCase.kt @@ -2,9 +2,7 @@ package io.timemates.app.users.usecases import io.timemates.app.users.repositories.TimersRepository import io.timemates.sdk.common.pagination.PagesIterator -import io.timemates.sdk.common.pagination.map import io.timemates.sdk.timers.types.Timer -import io.timemates.sdk.timers.types.value.TimerId class GetUserTimersUseCase( private val timers: TimersRepository, diff --git a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsScreen.kt b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsScreen.kt index af1592f..2ba3389 100644 --- a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsScreen.kt +++ b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsScreen.kt @@ -65,9 +65,10 @@ fun TimerSettingsScreen( LaunchedEffect(Unit) { stateMachine.effects.consumeEach { effect -> - when(effect) { + when (effect) { is Effect.Failure -> snackbarData.showSnackbar(message = strings.unknownFailure) + Effect.Success -> saveChanges() Effect.NavigateToTimersScreen -> navigateToTimersScreen() } diff --git a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/mvi/TimerSettingsMiddleware.kt b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/mvi/TimerSettingsMiddleware.kt index cd43ffc..2f4216a 100644 --- a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/mvi/TimerSettingsMiddleware.kt +++ b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/mvi/TimerSettingsMiddleware.kt @@ -7,7 +7,7 @@ import io.timemates.app.timers.ui.settings.mvi.TimerSettingsStateMachine.State class TimerSettingsMiddleware : Middleware { override fun onEffect(effect: Effect, store: StateStore): State { - return when(effect) { + return when (effect) { is Effect.Failure -> store.state.value.copy(isLoading = false) diff --git a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/mvi/TimerSettingsReducer.kt b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/mvi/TimerSettingsReducer.kt index 99894b3..65d3570 100644 --- a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/mvi/TimerSettingsReducer.kt +++ b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/settings/mvi/TimerSettingsReducer.kt @@ -64,8 +64,7 @@ class TimerSettingsReducer( } - - is Event.NameIsChanged -> + is Event.NameIsChanged -> state.copy(name = event.name, isNameSizeInvalid = false) is Event.DescriptionIsChanged -> @@ -102,12 +101,12 @@ class TimerSettingsReducer( sendEffect: (Effect) -> Unit, ) { coroutineScope.launch { - when(val result = timerSettingsUseCase.execute(timerId, newName, newDescription, settings)) { + when (val result = timerSettingsUseCase.execute(timerId, newName, newDescription, settings)) { is TimerSettingsUseCase.Result.Failure -> sendEffect(Effect.Failure(result.exception)) - is TimerSettingsUseCase.Result.Success -> - sendEffect(Effect.Success) + is TimerSettingsUseCase.Result.Success -> + sendEffect(Effect.Success) } } } diff --git a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/TimerCreationScreen.kt b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/TimerCreationScreen.kt index 6d94c95..fddecbf 100644 --- a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/TimerCreationScreen.kt +++ b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/TimerCreationScreen.kt @@ -65,7 +65,7 @@ fun TimerCreationScreen( LaunchedEffect(Unit) { stateMachine.effects.consumeEach { effect -> - when(effect) { + when (effect) { is Effect.Failure -> snackbarData.showSnackbar(message = strings.unknownFailure) diff --git a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationMiddleware.kt b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationMiddleware.kt index 8c4296d..6b5eae7 100644 --- a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationMiddleware.kt +++ b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationMiddleware.kt @@ -2,12 +2,12 @@ package io.timemates.app.timers.ui.timer_creation.mvi import io.timemates.app.foundation.mvi.Middleware import io.timemates.app.foundation.mvi.StateStore -import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.State import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.Effect +import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.State class TimerCreationMiddleware : Middleware { override fun onEffect(effect: Effect, store: StateStore): State { - return when(effect) { + return when (effect) { is Effect.Failure -> store.state.value.copy(isLoading = false) diff --git a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationReducer.kt b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationReducer.kt index c770f4e..636facd 100644 --- a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationReducer.kt +++ b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationReducer.kt @@ -59,7 +59,7 @@ class TimerCreationReducer( return state.copy(isLoading = true) } - is Event.NameIsChanged -> + is Event.NameIsChanged -> state.copy(name = event.name, isNameSizeInvalid = false) is Event.DescriptionIsChanged -> diff --git a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationStateMachine.kt b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationStateMachine.kt index c6635a5..627d826 100644 --- a/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationStateMachine.kt +++ b/feature/timers/presentation/src/commonMain/kotlin/io/timemates/app/timers/ui/timer_creation/mvi/TimerCreationStateMachine.kt @@ -5,13 +5,13 @@ import io.timemates.app.foundation.mvi.StateMachine import io.timemates.app.foundation.mvi.UiEffect import io.timemates.app.foundation.mvi.UiEvent import io.timemates.app.foundation.mvi.UiState +import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.Effect +import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.Event +import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.State import io.timemates.sdk.common.constructor.createOrThrow import io.timemates.sdk.common.types.value.Count import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes -import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.State -import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.Event -import io.timemates.app.timers.ui.timer_creation.mvi.TimerCreationStateMachine.Effect class TimerCreationStateMachine( reducer: TimerCreationReducer, diff --git a/feature/timers/presentation/src/jvmTest/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsMiddlewareTest.kt b/feature/timers/presentation/src/jvmTest/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsMiddlewareTest.kt index 0b41d30..265f891 100644 --- a/feature/timers/presentation/src/jvmTest/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsMiddlewareTest.kt +++ b/feature/timers/presentation/src/jvmTest/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsMiddlewareTest.kt @@ -5,7 +5,6 @@ import io.mockk.mockk import io.timemates.app.foundation.mvi.StateStore import io.timemates.app.timers.ui.settings.mvi.TimerSettingsMiddleware import io.timemates.app.timers.ui.settings.mvi.TimerSettingsStateMachine -import io.timemates.sdk.timers.types.Timer import kotlinx.coroutines.flow.MutableStateFlow import org.junit.jupiter.api.Test diff --git a/feature/timers/presentation/src/jvmTest/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsReducerTest.kt b/feature/timers/presentation/src/jvmTest/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsReducerTest.kt index c5fda59..1d420d7 100644 --- a/feature/timers/presentation/src/jvmTest/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsReducerTest.kt +++ b/feature/timers/presentation/src/jvmTest/kotlin/io/timemates/app/timers/ui/settings/TimerSettingsReducerTest.kt @@ -3,8 +3,8 @@ package io.timemates.app.timers.ui.settings import io.mockk.every import io.mockk.mockk import io.timemates.app.timers.ui.settings.mvi.TimerSettingsReducer -import io.timemates.app.timers.ui.settings.mvi.TimerSettingsStateMachine.State import io.timemates.app.timers.ui.settings.mvi.TimerSettingsStateMachine.Event +import io.timemates.app.timers.ui.settings.mvi.TimerSettingsStateMachine.State import io.timemates.app.users.usecases.TimerSettingsUseCase import io.timemates.app.users.validation.TimerDescriptionValidator import io.timemates.app.users.validation.TimerNameValidator diff --git a/feature/users/data/database/src/commonMain/sqldelight/io/timemates/app/users/data/database/CachedUsers.sq b/feature/users/data/database/src/commonMain/sqldelight/io/timemates/app/users/data/database/CachedUsers.sq index cab15be..b62778d 100644 --- a/feature/users/data/database/src/commonMain/sqldelight/io/timemates/app/users/data/database/CachedUsers.sq +++ b/feature/users/data/database/src/commonMain/sqldelight/io/timemates/app/users/data/database/CachedUsers.sq @@ -2,15 +2,14 @@ CREATE TABLE CachedUsers ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, - avatarFileId TEXT, gravatarId TEXT, emailAddress TEXT, lastQueryTime INTEGER NOT NULL ); insert: -INSERT INTO CachedUsers (id, name, description, avatarFileId, gravatarId, emailAddress, lastQueryTime) -VALUES (?, ?, ?, ?, ?, ?, ?); +INSERT INTO CachedUsers (id, name, description, gravatarId, emailAddress, lastQueryTime) +VALUES (?, ?, ?, ?, ?, ?); delete: DELETE FROM CachedUsers diff --git a/feature/users/data/src/commonMain/kotlin/io/timemates/app/users/data/CachedUsersDataSource.kt b/feature/users/data/src/commonMain/kotlin/io/timemates/app/users/data/CachedUsersDataSource.kt index 16229da..03e7677 100644 --- a/feature/users/data/src/commonMain/kotlin/io/timemates/app/users/data/CachedUsersDataSource.kt +++ b/feature/users/data/src/commonMain/kotlin/io/timemates/app/users/data/CachedUsersDataSource.kt @@ -3,16 +3,13 @@ package io.timemates.app.users.data import io.timemates.app.users.data.database.CachedUsersQueries import io.timemates.sdk.common.constructor.createOrNull import io.timemates.sdk.common.constructor.createOrThrow -import io.timemates.sdk.files.types.value.FileId import io.timemates.sdk.users.profile.types.Avatar import io.timemates.sdk.users.profile.types.User import io.timemates.sdk.users.profile.types.value.EmailAddress import io.timemates.sdk.users.profile.types.value.UserDescription import io.timemates.sdk.users.profile.types.value.UserId import io.timemates.sdk.users.profile.types.value.UserName -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class CachedUsersDataSource( @@ -26,9 +23,8 @@ class CachedUsersDataSource( cachedUsersQueries.insert( id = user.id.long, name = user.name.string, - description = user.description.string, - avatarFileId = (user.avatar as? Avatar.FileId)?.string, - gravatarId = (user.avatar as? Avatar.FileId)?.string, + description = user.description?.string.orEmpty(), + gravatarId = (user.avatar as? Avatar.GravatarId)?.string, emailAddress = user.emailAddress?.string, lastQueryTime = lastQueryTime, ) @@ -43,12 +39,12 @@ class CachedUsersDataSource( } suspend fun getUser(id: UserId): User? { - val user = withContext(Dispatchers.IO) { cachedUsersQueries.get(id.long).executeAsOneOrNull() } ?: return null + val user = withContext(Dispatchers.IO) { cachedUsersQueries.get(id.long).executeAsOneOrNull() } + ?: return null val name = UserName.createOrNull(user.name) ?: return null val description = UserDescription.createOrNull(user.description) ?: return null - val avatar = user.avatarFileId?.let { Avatar.FileId.createOrThrow(it) } - ?: user.gravatarId?.let { Avatar.GravatarId.createOrThrow(it) } + val avatar = user.gravatarId?.let { Avatar.GravatarId.createOrThrow(it) } val emailAddress = user.emailAddress?.let { emailAddress -> EmailAddress.createOrNull(emailAddress) } return User( diff --git a/feature/users/data/src/commonMain/kotlin/io/timemates/app/users/data/UsersRepository.kt b/feature/users/data/src/commonMain/kotlin/io/timemates/app/users/data/UsersRepository.kt index 442d14e..c16c5cd 100644 --- a/feature/users/data/src/commonMain/kotlin/io/timemates/app/users/data/UsersRepository.kt +++ b/feature/users/data/src/commonMain/kotlin/io/timemates/app/users/data/UsersRepository.kt @@ -18,7 +18,7 @@ class UsersRepository( private val cachedUsersDataSource: CachedUsersDataSource, private val timeProvider: TimeProvider, private val coroutineScope: CoroutineScope, -): UserRepositoryContract { +) : UserRepositoryContract { init { coroutineScope.launch { @@ -28,13 +28,13 @@ class UsersRepository( override suspend fun getUser(id: UserId): Flow> = flow { // Emit local saved data (if have) - if(cachedUsersDataSource.isHaveUser(id)) + if (cachedUsersDataSource.isHaveUser(id)) cachedUsersDataSource.getUser(id)?.let { emit(Result.success(it)) } // Load actual data userApi.profile.getProfiles(listOf(id)) .map { users -> - if(users.isEmpty()) + if (users.isEmpty()) Result.success(users.first()) else Result.success(users.first()) @@ -49,13 +49,13 @@ class UsersRepository( override suspend fun getUsers(ids: List): Flow>> = flow { // Emit local saved data (if have) val cachedUsers = cachedUsersDataSource.getUsers(ids) - if(cachedUsers.isNotEmpty()) + if (cachedUsers.isNotEmpty()) emit(Result.success(cachedUsers)) // Load actual data userApi.profile.getProfiles(ids) .map { users -> - if(users.isEmpty()) + if (users.isEmpty()) Result.success(users) else Result.success(users) diff --git a/feature/users/domain/src/commonMain/kotlin/io/timemates/app/users/repositories/UsersRepository.kt b/feature/users/domain/src/commonMain/kotlin/io/timemates/app/users/repositories/UsersRepository.kt index 58df418..0803569 100644 --- a/feature/users/domain/src/commonMain/kotlin/io/timemates/app/users/repositories/UsersRepository.kt +++ b/feature/users/domain/src/commonMain/kotlin/io/timemates/app/users/repositories/UsersRepository.kt @@ -1,13 +1,11 @@ package io.timemates.app.users.repositories -import io.timemates.sdk.common.constructor.createOrThrow import io.timemates.sdk.common.types.Empty import io.timemates.sdk.users.profile.types.User import io.timemates.sdk.users.profile.types.value.UserDescription import io.timemates.sdk.users.profile.types.value.UserId import io.timemates.sdk.users.profile.types.value.UserName import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch /** * Interface representing a repository for user-related operations. diff --git a/feature/users/domain/src/commonMain/kotlin/io/timemates/app/users/usecases/GetUserUseCase.kt b/feature/users/domain/src/commonMain/kotlin/io/timemates/app/users/usecases/GetUserUseCase.kt index 55746b1..1960a51 100644 --- a/feature/users/domain/src/commonMain/kotlin/io/timemates/app/users/usecases/GetUserUseCase.kt +++ b/feature/users/domain/src/commonMain/kotlin/io/timemates/app/users/usecases/GetUserUseCase.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.map * @param repository The repository for user operations. */ class GetUserUseCase( - private val repository: UsersRepository + private val repository: UsersRepository, ) { /** * Executes the use case to retrieve a user with the specified ID. diff --git a/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/EditUserUseCaseTest.kt b/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/EditUserUseCaseTest.kt index 9a8fcd4..5a60181 100644 --- a/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/EditUserUseCaseTest.kt +++ b/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/EditUserUseCaseTest.kt @@ -5,6 +5,7 @@ import io.mockk.mockk import io.timemates.app.users.repositories.AuthorizationsRepository import io.timemates.app.users.repositories.UsersRepository import io.timemates.sdk.common.constructor.createOrThrow +import io.timemates.sdk.common.types.Empty import io.timemates.sdk.users.profile.types.value.UserDescription import io.timemates.sdk.users.profile.types.value.UserId import io.timemates.sdk.users.profile.types.value.UserName @@ -25,7 +26,7 @@ class EditUserUseCaseTest { val name = UserName.createOrThrow("John Doe") val description = UserDescription.createOrThrow("Some description") coEvery { authorizations.getMe() } returns Result.success(userId) - coEvery { repository.editUser(name, description) } returns Result.success(Unit) + coEvery { repository.editUser(name, description) } returns Result.success(Empty) // WHEN val result = runBlocking { useCase.execute(name, description) } diff --git a/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/GetUserUseCaseTest.kt b/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/GetUserUseCaseTest.kt index 1865fa5..7ae06ce 100644 --- a/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/GetUserUseCaseTest.kt +++ b/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/GetUserUseCaseTest.kt @@ -5,12 +5,13 @@ import io.mockk.mockk import io.timemates.app.users.repositories.UsersRepository import io.timemates.sdk.common.constructor.createOrThrow import io.timemates.sdk.common.exceptions.NotFoundException -import io.timemates.sdk.files.types.value.FileId import io.timemates.sdk.users.profile.types.User import io.timemates.sdk.users.profile.types.value.UserDescription import io.timemates.sdk.users.profile.types.value.UserId import io.timemates.sdk.users.profile.types.value.UserName -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals @@ -20,58 +21,60 @@ class GetUserUseCaseTest { private val useCase = GetUserUseCase(repository) @Test - fun `execute with valid userId should return Success result`() { + fun `execute with valid userId should return Success result`(): Unit = runTest { // GIVEN val userId = UserId.createOrThrow(1) val user = User( id = userId, name = UserName.createOrThrow("John"), description = UserDescription.createOrThrow("Description"), - avatarFileId = FileId.createOrThrow("..."), + avatar = null, emailAddress = null, ) - coEvery { repository.getUser(userId) } returns Result.success(user) + coEvery { repository.getUser(userId) } returns flowOf(Result.success(user)) // WHEN - val result = runBlocking { useCase.execute(userId) } + val result = useCase.execute(userId) // THEN assertEquals( expected = GetUserUseCase.Result.Success(user), - actual = result, + actual = result.first(), ) } @Test - fun `execute with NotFoundException should return NotFound result`() { + fun `execute with NotFoundException should return NotFound result`(): Unit = runTest { // GIVEN val userId = UserId.createOrThrow(1) - coEvery { repository.getUser(userId) } returns Result.failure(NotFoundException("User not found")) + coEvery { repository.getUser(userId) } returns flowOf(Result.failure(NotFoundException("User not found"))) // WHEN - val result = runBlocking { useCase.execute(userId) } + val result = useCase.execute(userId) // THEN - assertEquals( - expected = GetUserUseCase.Result.NotFound, - actual = result, - ) + runTest { + assertEquals( + expected = GetUserUseCase.Result.NotFound, + actual = result.first(), + ) + } } @Test - fun `execute with other exceptions should return Failure result`() { + fun `execute with other exceptions should return Failure result`(): Unit = runTest { // GIVEN val userId = UserId.createOrThrow(1) val exception = Exception("Failed to retrieve user") - coEvery { repository.getUser(userId) } returns Result.failure(exception) + coEvery { repository.getUser(userId) } returns flowOf(Result.failure(exception)) // WHEN - val result = runBlocking { useCase.execute(userId) } + val result = useCase.execute(userId) // THEN assertEquals( expected = GetUserUseCase.Result.Failure(exception), - actual = result, + actual = result.first(), ) } } diff --git a/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/GetUsersUseCaseTest.kt b/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/GetUsersUseCaseTest.kt index c436d63..312024b 100644 --- a/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/GetUsersUseCaseTest.kt +++ b/feature/users/domain/src/jvmTest/kotlin/io/timemates/app/users/usecases/GetUsersUseCaseTest.kt @@ -5,12 +5,13 @@ import io.mockk.mockk import io.timemates.app.users.repositories.UsersRepository import io.timemates.sdk.common.constructor.createOrThrow import io.timemates.sdk.common.exceptions.NotFoundException -import io.timemates.sdk.files.types.value.FileId import io.timemates.sdk.users.profile.types.User import io.timemates.sdk.users.profile.types.value.UserDescription import io.timemates.sdk.users.profile.types.value.UserId import io.timemates.sdk.users.profile.types.value.UserName -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals @@ -20,7 +21,7 @@ class GetUsersUseCaseTest { private val useCase = GetUsersUseCase(repository) @Test - fun `execute with valid userIds should return Success result`() { + fun `execute with valid userIds should return Success result`(): Unit = runTest { // GIVEN val userIds = listOf(UserId.createOrThrow(1), UserId.createOrThrow(2)) val users = listOf( @@ -28,59 +29,61 @@ class GetUsersUseCaseTest { id = userIds[0], name = UserName.createOrThrow("John"), description = UserDescription.createOrThrow("Description 1"), - avatarFileId = FileId.createOrThrow("..."), emailAddress = null, + avatar = null, ), User( id = userIds[1], name = UserName.createOrThrow("Jane"), description = UserDescription.createOrThrow("Description 2"), - avatarFileId = FileId.createOrThrow("..."), emailAddress = null, + avatar = null, ) ) - coEvery { repository.getUsers(userIds) } returns Result.success(users) + coEvery { repository.getUsers(userIds) } returns flowOf(Result.success(users)) // WHEN - val result = runBlocking { useCase.execute(userIds) } + val result = useCase.execute(userIds) // THEN - assertEquals( - expected = GetUsersUseCase.Result.Success(users), - actual = result, - ) + runTest { + assertEquals( + expected = GetUsersUseCase.Result.Success(users), + actual = result.first(), + ) + } } @Test - fun `execute with NotFoundException should return NotFound result`() { + fun `execute with NotFoundException should return NotFound result`(): Unit = runTest { // GIVEN val userIds = listOf(UserId.createOrThrow(1)) - coEvery { repository.getUsers(userIds) } returns Result.failure(NotFoundException("User not found")) + coEvery { repository.getUsers(userIds) } returns flowOf(Result.failure(NotFoundException("User not found"))) // WHEN - val result = runBlocking { useCase.execute(userIds) } + val result = useCase.execute(userIds) // THEN assertEquals( expected = GetUsersUseCase.Result.NotFound, - actual = result, + actual = result.first(), ) } @Test - fun `execute with other exceptions should return Failure result`() { + fun `execute with other exceptions should return Failure result`(): Unit = runTest { // GIVEN val userIds = listOf(UserId.createOrThrow(1)) val exception = Exception("Failed to retrieve users") - coEvery { repository.getUsers(userIds) } returns Result.failure(exception) + coEvery { repository.getUsers(userIds) } returns flowOf(Result.failure(exception)) // WHEN - val result = runBlocking { useCase.execute(userIds) } + val result = useCase.execute(userIds) // THEN assertEquals( expected = GetUsersUseCase.Result.Failure(exception), - actual = result, + actual = result.first(), ) } } diff --git a/foundation/mvi/koin-compose/build.gradle.kts b/foundation/mvi/koin-compose/build.gradle.kts index 2e6999e..602a530 100644 --- a/foundation/mvi/koin-compose/build.gradle.kts +++ b/foundation/mvi/koin-compose/build.gradle.kts @@ -26,11 +26,5 @@ dependencies { } android { - compileSdk = libs.versions.android.target.get().toInt() - - defaultConfig { - minSdk = libs.versions.android.min.get().toInt() - } - namespace = "io.timemates.app.mvi.compose" } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index b446b52..997a09d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,5 @@ org.gradle.daemon=true android.useAndroidX=true # org.gradle.unsafe.configuration-cache=true org.gradle.caching=true -kotlin.mpp.androidSourceSetLayoutVersion=2 \ No newline at end of file +kotlin.mpp.androidSourceSetLayoutVersion=2 +org.jetbrains.compose.experimental.jscanvas.enabled=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5a18270..41c8e44 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,30 +1,30 @@ [versions] -kotlin = "1.9.10" +kotlin = "1.9.21" kotlinx-coroutines = "1.7.3" -kotlinx-serialization = "1.5.0" -ktor = "2.0.1" +kotlinx-serialization = "1.6.0" +ktor = "2.3.7" jupiter = "5.8.2" exposed = "0.41.1" kmongo = "4.8.0" -androidGradlePlugin = "8.1.2" +androidGradlePlugin = "8.2.0" androidComposeVersion = "2.6.2" -jetpackComposeCompilerVersion = "1.5.3" -jetbrainsCompose = "1.5.2" -timeMatesSdk = "dev-ede0c538935ec8f2c452d7cfb4d6747a1a1c81c7" -ksp = "1.9.10-1.0.13" +jetpackComposeCompilerVersion = "1.5.6" +jetbrainsCompose = "1.5.11" +timeMatesSdk = "dev-adc3f9431bfb5f0e14e030d1ae6c8a7db0f6a1e2" +ksp = "1.9.21-1.0.16" android-target = "34" android-min = "24" -koin = "3.4.2" -koin-annotations = "1.2.2" -decompose = "2.0.0-beta-01" -sqldelight = "2.0.0" +koin = "3.5.3" +koin-annotations = "1.3.0" +decompose = "2.2.1" +sqldelight = "2.0.1" moko-resources = "0.23.0" jna = "5.13.0" core-ktx = "1.12.0" junit = "4.13.2" androidx-test-ext-junit = "1.1.5" espresso-core = "3.5.1" -material = "1.9.0" +material = "1.11.0" [libraries] kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } @@ -41,6 +41,8 @@ ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" } ktor-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } +ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" } + logback-classic = { module = "ch.qos.logback:logback-classic", version.require = "1.3.5" } litote-kmongo-async = { module = "org.litote.kmongo:kmongo-async", version.ref = "kmongo" } @@ -60,26 +62,25 @@ cache4k = { module = "io.github.reactivecircus.cache4k:cache4k", version.require androidx-compose-ui = { module = "androidx.compose.ui:ui", version.ref = "androidComposeVersion" } androidx-compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "androidComposeVersion" } -androidx-compose-material3 = { module = "androidx.compose.material3:material3", version.require = "1.1.1" } +androidx-compose-material3 = { module = "androidx.compose.material3:material3", version.require = "1.1.2" } androidx-compose-icons = { module = "androidx.compose.material:material-icons-core", version.ref = "androidComposeVersion" } androidx-compose-extendedIcons = { module = "androidx.compose.material:material-icons-extended", version.ref = "androidComposeVersion" } -androidx-compose-activity = { module = "androidx.activity:activity-compose", version.require = "1.8.0-alpha06" } +androidx-compose-activity = { module = "androidx.activity:activity-compose", version.require = "1.8.2" } androidx-compose-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidComposeVersion" } -androidx-compose-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "jetbrainsCompose" } -androidx-compose-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.require = "jetbrainsCompose" } +androidx-compose-tooling = { module = "androidx.compose.ui:ui-tooling", version.require = "1.5.4" } +androidx-compose-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.require = "1.5.4" } compose-accompanist-systemUiController = { module = "com.google.accompanist:accompanist-systemuicontroller", version.require = "0.31.6-rc" } -androidx-lifecycle = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.require = "2.6.1" } +androidx-lifecycle = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.require = "2.6.2" } timemates-sdk = { module = "io.timemates:sdk", version.ref = "timeMatesSdk" } -timemates-engine-grpc = { module = "io.timemates:grpc-engine", version.ref = "timeMatesSdk" } -timemates-engine-grpc-android = { module = "io.timemates:grpc-engine-android", version.ref = "timeMatesSdk" } +timemates-engine-rsocket = { module = "io.timemates:rsocket-engine", version.ref = "timeMatesSdk" } koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin-annotations" } koin-ksp-compiler = { module = "io.insert-koin:koin-ksp-compiler", version.ref = "koin-annotations" } -androidx-appcompat = { module = "androidx.appcompat:appcompat", version.require = "1.7.0-alpha02" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.require = "1.7.0-alpha03" } decompose = { module = "com.arkivanov.decompose:decompose", version.ref = "decompose" } decompose-jetbrains-compose = { module = "com.arkivanov.decompose:extensions-compose-jetbrains", version.ref = "decompose" } koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.require = "3.4.5" } @@ -106,6 +107,8 @@ espresso-core = { group = "androidx.test.espresso", name = "espresso-core", vers material = { group = "com.google.android.material", name = "material", version.ref = "material" } mockk = { group = "io.mockk", name = "mockk", version.require = "1.13.5" } +libres-compose = { module = "io.github.skeptick.libres:libres-compose", version.require = "1.2.2" } + [plugins] kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } @@ -126,4 +129,5 @@ configurations-koin-annotations = { id = "koin-dependencies-convention", version configurations-unit-tests = { id = "unit-tests-convention", version.require = "SNAPSHOT" } configurations-unit-tests-mockable = { id = "unit-tests-with-mockk-convention", version.require = "SNAPSHOT" } configurations-jvm-library = { id = "jvm-library-convention", version.require = "SNAPSHOT" } -moko-multiplatform-resources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko-resources" } \ No newline at end of file +moko-multiplatform-resources = { id = "dev.icerock.mobile.multiplatform-resources", version.ref = "moko-resources" } +libres = { id = "io.github.skeptick.libres", version.require = "1.2.2" } \ No newline at end of file diff --git a/platforms/android/build.gradle.kts b/platforms/android/build.gradle.kts index 5f0fa77..541f418 100644 --- a/platforms/android/build.gradle.kts +++ b/platforms/android/build.gradle.kts @@ -52,13 +52,14 @@ kotlin { dependencies { implementation(libs.timemates.sdk) - implementation(libs.timemates.engine.grpc) - implementation(libs.timemates.engine.grpc.android) + implementation(libs.timemates.engine.rsocket) implementation(libs.koin.core) implementation(libs.androidx.appcompat) implementation(libs.android.multidex) + implementation(libs.ktor.client.cio) + implementation(libs.sqldelight.android.driver) implementation(projects.feature.common.domain) @@ -67,8 +68,6 @@ dependencies { implementation(projects.feature.authorization.dependencies) implementation(projects.feature.authorization.data.database) - implementation(libs.grpc.okhttp) - implementation(libs.compose.accompanist.systemUiController) implementation(projects.foundation.time) diff --git a/platforms/android/src/main/kotlin/io/timemates/app/TimeMatesApplication.kt b/platforms/android/src/main/kotlin/io/timemates/app/TimeMatesApplication.kt index b946c25..1f48287 100644 --- a/platforms/android/src/main/kotlin/io/timemates/app/TimeMatesApplication.kt +++ b/platforms/android/src/main/kotlin/io/timemates/app/TimeMatesApplication.kt @@ -3,10 +3,10 @@ package io.timemates.app import android.content.Context import androidx.multidex.MultiDex import androidx.multidex.MultiDexApplication +import app.cash.sqldelight.async.coroutines.synchronous import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.android.AndroidSqliteDriver -import io.timemates.android.grpc.AndroidGrpcEngineBuilder -import io.timemates.api.grpc.GrpcTimeMatesRequestsEngine +import io.timemates.api.rsocket.RSocketTimeMatesRequestsEngine import io.timemates.app.authorization.dependencies.AuthorizationDataModule import io.timemates.app.authorization.dependencies.screens.AfterStartModule import io.timemates.app.authorization.dependencies.screens.ConfigureAccountModule @@ -19,6 +19,8 @@ import io.timemates.app.foundation.time.TimeProvider import io.timemates.app.users.data.database.TimeMatesUsers import io.timemates.data.database.TimeMatesAuthorizations import io.timemates.sdk.common.engine.TimeMatesRequestsEngine +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import org.koin.core.context.startKoin import org.koin.core.qualifier.qualifier @@ -38,9 +40,7 @@ class TimeMatesApplication : MultiDexApplication() { startKoin { val platformModule = module { single { - GrpcTimeMatesRequestsEngine( - grpcEngineBuilder = AndroidGrpcEngineBuilder(applicationContext) - ) + RSocketTimeMatesRequestsEngine(coroutineScope = CoroutineScope(Dispatchers.IO)) } single { @@ -51,11 +51,19 @@ class TimeMatesApplication : MultiDexApplication() { } single(qualifier = qualifier("authorization")) { - AndroidSqliteDriver(TimeMatesAuthorizations.Schema, applicationContext, "authorizations.db") + AndroidSqliteDriver( + schema = TimeMatesAuthorizations.Schema.synchronous(), + context = applicationContext, + name = "authorizations.db", + ) } single(qualifier = qualifier("users")) { - AndroidSqliteDriver(TimeMatesUsers.Schema, applicationContext, "users.db") + AndroidSqliteDriver( + schema = TimeMatesUsers.Schema.synchronous(), + context = applicationContext, + name = "users.db", + ) } single { diff --git a/platforms/desktop/build.gradle.kts b/platforms/desktop/build.gradle.kts index d7da124..452a01e 100644 --- a/platforms/desktop/build.gradle.kts +++ b/platforms/desktop/build.gradle.kts @@ -15,7 +15,9 @@ dependencies { implementation(projects.feature.common.domain) implementation(libs.timemates.sdk) - implementation(libs.timemates.engine.grpc) + implementation(libs.timemates.engine.rsocket) + + implementation(libs.ktor.client.cio) implementation(projects.feature.users.data.database) implementation(projects.feature.authorization.dependencies) diff --git a/platforms/desktop/src/main/kotlin/io/timemates/app/Dependencies.kt b/platforms/desktop/src/main/kotlin/io/timemates/app/Dependencies.kt index 77b1b7a..cef3448 100644 --- a/platforms/desktop/src/main/kotlin/io/timemates/app/Dependencies.kt +++ b/platforms/desktop/src/main/kotlin/io/timemates/app/Dependencies.kt @@ -2,8 +2,7 @@ package io.timemates.app import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver -import io.timemates.api.grpc.GrpcTimeMatesRequestsEngine -import io.timemates.api.grpc.factory.DefaultGrpcEngineBuilder +import io.timemates.api.rsocket.RSocketTimeMatesRequestsEngine import io.timemates.app.authorization.dependencies.AuthorizationDataModule import io.timemates.app.authorization.dependencies.screens.AfterStartModule import io.timemates.app.authorization.dependencies.screens.ConfigureAccountModule @@ -15,6 +14,8 @@ import io.timemates.app.core.handler.OnAuthorizationFailedHandler import io.timemates.app.foundation.time.SystemUTCTimeProvider import io.timemates.app.foundation.time.TimeProvider import io.timemates.sdk.common.engine.TimeMatesRequestsEngine +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import org.koin.core.context.startKoin import org.koin.core.qualifier.qualifier @@ -42,8 +43,9 @@ fun initializeDependencies( } single { - GrpcTimeMatesRequestsEngine( - grpcEngineBuilder = DefaultGrpcEngineBuilder() + RSocketTimeMatesRequestsEngine( + endpoint = "wss://api.timemates.io/rsocket", + coroutineScope = CoroutineScope(Dispatchers.IO), ) } diff --git a/style-system/build.gradle.kts b/style-system/build.gradle.kts index bda2c32..7f7384e 100644 --- a/style-system/build.gradle.kts +++ b/style-system/build.gradle.kts @@ -1,44 +1,16 @@ plugins { id(libs.plugins.configurations.compose.multiplatform.get().pluginId) - alias(libs.plugins.moko.multiplatform.resources) -} - -kotlin { - jvm() - androidTarget() - - sourceSets { - val commonMain by getting { - kotlin.srcDir("build/generated/moko/commonMain") - } - - val androidMain by getting { - kotlin.srcDir("build/generated/moko/androidMain") - } - - val jvmMain by getting { - kotlin.srcDir("build/generated/moko/jvmMain") - } - } + alias(libs.plugins.libres) } dependencies { - commonMainApi(compose.ui) - commonMainImplementation(compose.material) - commonMainApi(compose.material3) - commonMainApi(compose.animation) - commonMainApi(compose.foundation) - commonMainApi(compose.materialIconsExtended) - - commonMainImplementation(libs.moko.resources) - commonMainImplementation(libs.moko.resources.compose) + commonMainApi(libs.libres.compose) } android { namespace = "io.timemates.app.style.system" } -multiplatformResources { - multiplatformResourcesPackage = "io.timemates.app.style.system" - multiplatformResourcesClassName = "Resources" +libres { + generatedClassName = "Resources" } \ No newline at end of file diff --git a/style-system/src/commonMain/kotlin/io/timemates/app/style/system/appbar/AppBar.kt b/style-system/src/commonMain/kotlin/io/timemates/app/style/system/appbar/AppBar.kt index 1caa72a..bd454e6 100644 --- a/style-system/src/commonMain/kotlin/io/timemates/app/style/system/appbar/AppBar.kt +++ b/style-system/src/commonMain/kotlin/io/timemates/app/style/system/appbar/AppBar.kt @@ -47,7 +47,6 @@ internal fun AppBarText( text = title, modifier = modifier, fontSize = 24.sp, - fontFamily = AppTheme.fonts.Inter, fontWeight = FontWeight.ExtraBold, color = AppTheme.colors.primary, ) diff --git a/style-system/src/commonMain/kotlin/io/timemates/app/style/system/images/CircleIcon.kt b/style-system/src/commonMain/kotlin/io/timemates/app/style/system/images/CircleIcon.kt index d0e8099..bdf005e 100644 --- a/style-system/src/commonMain/kotlin/io/timemates/app/style/system/images/CircleIcon.kt +++ b/style-system/src/commonMain/kotlin/io/timemates/app/style/system/images/CircleIcon.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon +import androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/style-system/src/commonMain/kotlin/io/timemates/app/style/system/theme/AppFont.kt b/style-system/src/commonMain/kotlin/io/timemates/app/style/system/theme/AppFont.kt deleted file mode 100644 index f0e6d56..0000000 --- a/style-system/src/commonMain/kotlin/io/timemates/app/style/system/theme/AppFont.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.timemates.app.style.system.theme - -import androidx.compose.runtime.Composable -import androidx.compose.runtime.ProvidableCompositionLocal -import androidx.compose.runtime.Stable -import androidx.compose.runtime.staticCompositionLocalOf -import androidx.compose.ui.text.font.FontFamily -import dev.icerock.moko.resources.compose.asFont -import io.timemates.app.style.system.Resources - -data class AppFonts internal constructor( - val Inter: FontFamily, -) - -@Stable -@Composable -fun appFonts(): AppFonts { - return AppFonts( - FontFamily( - Resources.fonts.Inter.black.asFont()!!, - Resources.fonts.Inter.bold.asFont()!!, - Resources.fonts.Inter.extraBold.asFont()!!, - Resources.fonts.Inter.extraLight.asFont()!!, - Resources.fonts.Inter.light.asFont()!!, - Resources.fonts.Inter.medium.asFont()!!, - Resources.fonts.Inter.regular.asFont()!!, - Resources.fonts.Inter.semiBold.asFont()!!, - ) - ) -} - -val LocalAppFonts: ProvidableCompositionLocal = staticCompositionLocalOf { - error("LocalAppFonts is not provided") -} \ No newline at end of file diff --git a/style-system/src/commonMain/kotlin/io/timemates/app/style/system/theme/AppTheme.kt b/style-system/src/commonMain/kotlin/io/timemates/app/style/system/theme/AppTheme.kt index 334fb67..1821a5e 100644 --- a/style-system/src/commonMain/kotlin/io/timemates/app/style/system/theme/AppTheme.kt +++ b/style-system/src/commonMain/kotlin/io/timemates/app/style/system/theme/AppTheme.kt @@ -20,16 +20,6 @@ object AppTheme { val colors: AppColors @[Composable ReadOnlyComposable] get() = LocalAppColors.current - - /** - * The [AppFonts] instance representing the fonts that used in app. - * The fonts read from the [LocalAppFonts]. - * - * @see LocalAppFonts - */ - val fonts: AppFonts - @[Composable ReadOnlyComposable] - get() = LocalAppFonts.current } @@ -46,7 +36,6 @@ fun AppTheme( CompositionLocalProvider( LocalAppColors provides lightColors(), - LocalAppFonts provides appFonts(), ) { MaterialTheme(colorScheme = colors) { Surface { diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-Black.ttf b/style-system/src/commonMain/libres/fonts/Inter-Black.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-Black.ttf rename to style-system/src/commonMain/libres/fonts/Inter-Black.ttf diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-Bold.ttf b/style-system/src/commonMain/libres/fonts/Inter-Bold.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-Bold.ttf rename to style-system/src/commonMain/libres/fonts/Inter-Bold.ttf diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-ExtraBold.ttf b/style-system/src/commonMain/libres/fonts/Inter-ExtraBold.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-ExtraBold.ttf rename to style-system/src/commonMain/libres/fonts/Inter-ExtraBold.ttf diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-ExtraLight.ttf b/style-system/src/commonMain/libres/fonts/Inter-ExtraLight.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-ExtraLight.ttf rename to style-system/src/commonMain/libres/fonts/Inter-ExtraLight.ttf diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-Light.ttf b/style-system/src/commonMain/libres/fonts/Inter-Light.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-Light.ttf rename to style-system/src/commonMain/libres/fonts/Inter-Light.ttf diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-Medium.ttf b/style-system/src/commonMain/libres/fonts/Inter-Medium.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-Medium.ttf rename to style-system/src/commonMain/libres/fonts/Inter-Medium.ttf diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-Regular.ttf b/style-system/src/commonMain/libres/fonts/Inter-Regular.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-Regular.ttf rename to style-system/src/commonMain/libres/fonts/Inter-Regular.ttf diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-SemiBold.ttf b/style-system/src/commonMain/libres/fonts/Inter-SemiBold.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-SemiBold.ttf rename to style-system/src/commonMain/libres/fonts/Inter-SemiBold.ttf diff --git a/style-system/src/commonMain/resources/MR/fonts/Inter-Thin.ttf b/style-system/src/commonMain/libres/fonts/Inter-Thin.ttf similarity index 100% rename from style-system/src/commonMain/resources/MR/fonts/Inter-Thin.ttf rename to style-system/src/commonMain/libres/fonts/Inter-Thin.ttf diff --git a/style-system/src/commonMain/resources/MR/images/confirm_authorization_info_image.svg b/style-system/src/commonMain/libres/images/confirm_authorization_info_image.svg similarity index 100% rename from style-system/src/commonMain/resources/MR/images/confirm_authorization_info_image.svg rename to style-system/src/commonMain/libres/images/confirm_authorization_info_image.svg diff --git a/style-system/src/commonMain/resources/MR/images/initial_screen_image.svg b/style-system/src/commonMain/libres/images/initial_screen_image.svg similarity index 100% rename from style-system/src/commonMain/resources/MR/images/initial_screen_image.svg rename to style-system/src/commonMain/libres/images/initial_screen_image.svg diff --git a/style-system/src/commonMain/resources/MR/images/new_account_info_image.svg b/style-system/src/commonMain/libres/images/new_account_info_image.svg similarity index 100% rename from style-system/src/commonMain/resources/MR/images/new_account_info_image.svg rename to style-system/src/commonMain/libres/images/new_account_info_image.svg