From 3a9aa241eefe3ccdb31ac0fdbfedde10a37de9f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:08:48 +0100 Subject: [PATCH 01/16] Bump plugin.serialization from 2.0.21 to 2.1.0 (#238) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- buildSrc/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c44669432..565a088dc 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,7 +1,7 @@ plugins { `kotlin-dsl` kotlin("jvm") version "2.1.0" - kotlin("plugin.serialization") version "2.0.21" + kotlin("plugin.serialization") version "2.1.0" // id("java-gradle-plugin") } From eedaed5eb8960b10045bb51949470c9f00012188 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:31:22 +0100 Subject: [PATCH 02/16] Bump io.sentry.android.gradle from 4.14.0 to 4.14.1 (#237) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Felber --- app/build.gradle.kts | 2 +- .../feature/coremodulestest/SyncServerTimeE2eTest.kt | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index de77c2c8b..c3074014a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,7 +14,7 @@ plugins { id("com.google.gms.google-services") id("com.google.firebase.appdistribution") id("com.google.android.gms.oss-licenses-plugin") - id("io.sentry.android.gradle") version "4.14.0" + id("io.sentry.android.gradle") version "4.14.1" id("artemis.android.room") } diff --git a/feature/core-modules-test/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/coremodulestest/SyncServerTimeE2eTest.kt b/feature/core-modules-test/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/coremodulestest/SyncServerTimeE2eTest.kt index ba7232d27..4f2406ad9 100644 --- a/feature/core-modules-test/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/coremodulestest/SyncServerTimeE2eTest.kt +++ b/feature/core-modules-test/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/coremodulestest/SyncServerTimeE2eTest.kt @@ -5,9 +5,8 @@ import de.tum.informatics.www1.artemis.native_app.core.common.test.DefaultTestTi import de.tum.informatics.www1.artemis.native_app.core.common.test.EndToEndTest import de.tum.informatics.www1.artemis.native_app.core.common.test.testServerUrl import de.tum.informatics.www1.artemis.native_app.core.data.dataModule -import de.tum.informatics.www1.artemis.native_app.core.data.service.impl.JsonProvider import de.tum.informatics.www1.artemis.native_app.core.data.service.network.ServerTimeService -import de.tum.informatics.www1.artemis.native_app.core.data.test.service.TrustAllCertsKtorProvider +import de.tum.informatics.www1.artemis.native_app.core.test.BaseComposeTest import de.tum.informatics.www1.artemis.native_app.core.test.coreTestModules import de.tum.informatics.www1.artemis.native_app.core.test.test_setup.DefaultTimeoutMillis import de.tum.informatics.www1.artemis.native_app.feature.login.loginModule @@ -16,15 +15,12 @@ import de.tum.informatics.www1.artemis.native_app.feature.login.test.testLoginMo import kotlinx.coroutines.async import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.take -import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test import org.junit.experimental.categories.Category import org.junit.runner.RunWith import org.koin.android.ext.koin.androidContext -import org.koin.test.KoinTest import org.koin.test.KoinTestRule import org.robolectric.RobolectricTestRunner import kotlin.time.Duration.Companion.milliseconds @@ -32,7 +28,7 @@ import kotlin.time.Duration.Companion.seconds @Category(EndToEndTest::class) @RunWith(RobolectricTestRunner::class) -class SyncServerTimeE2eTest : KoinTest { +class SyncServerTimeE2eTest : BaseComposeTest() { @get:Rule val koinRule = KoinTestRule.create { @@ -43,7 +39,7 @@ class SyncServerTimeE2eTest : KoinTest { } @Test(timeout = DefaultTestTimeoutMillis) - fun `sync server time`() = runTest(timeout = DefaultTimeoutMillis.milliseconds) { + fun `sync server time`() = runTest(timeout = DefaultTimeoutMillis.milliseconds * 2) { // Multiplied by 2, because flaky test val serverTimeService: ServerTimeService = koinRule.koin.get() val accessToken = performTestLogin() From 6e4837358275392d8e6ce7430fd50794da4def8a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 10:31:32 +0100 Subject: [PATCH 03/16] Bump androidx.paging:paging-compose from 3.3.4 to 3.3.5 (#236) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Felber <45291671+FelberMartin@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 57972ba76..e916f740c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ androidxDataStore = "1.1.1" androidxLifecycle = "2.8.7" androidxNavigation = "2.8.4" androidxPaging = "3.3.5" -androidxPagingCompose = "3.3.4" +androidxPagingCompose = "3.3.5" coil = "3.0.4" coil2 = "2.7.0" # The markwon library still depends on coil2: https://github.com/ls1intum/artemis-android/issues/171 emoji2 = "1.5.0" From f0e20134b93144674cf2c6d7d86bf7282605ad83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:02:43 +0100 Subject: [PATCH 04/16] Bump ktor from 3.0.1 to 3.0.2 (#230) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Felber <45291671+FelberMartin@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e916f740c..c67bc3889 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ koin = "4.0.0" koinAndroidxCompose = "4.0.0" kover = "0.9.0" krossbow = "8.1.0" -ktor = "3.0.1" +ktor = "3.0.2" markwon = "4.6.2" mockk = "1.13.13" ossLicensesPlugin = "0.10.6" From 32208e322ddaf47de351bb06152a4f40250e93fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:02:53 +0100 Subject: [PATCH 05/16] Bump androidx.compose:compose-bom from 2024.11.00 to 2024.12.01 (#231) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Felber <45291671+FelberMartin@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c67bc3889..32dad3368 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ androidDesugarJdkLibs = "2.1.3" androidGradlePlugin = "8.7.1" androidxActivity = "1.9.3" androidxAppCompat = "1.7.0" -androidxComposeBom = "2024.11.00" +androidxComposeBom = "2024.12.01" androidxDataStore = "1.1.1" androidxLifecycle = "2.8.7" androidxNavigation = "2.8.4" From 554f22742b6658e99cd530399aed4b40bc4a64a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 09:27:11 +0100 Subject: [PATCH 06/16] Bump mockk from 1.13.13 to 1.13.14 (#247) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 32dad3368..610f929b2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -28,7 +28,7 @@ kover = "0.9.0" krossbow = "8.1.0" ktor = "3.0.2" markwon = "4.6.2" -mockk = "1.13.13" +mockk = "1.13.14" ossLicensesPlugin = "0.10.6" placeholderMaterial = "2.0.0" room = "2.6.1" From 06984878cc07eb5f7e1f85f27eaf2acc8a03618e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 09:27:23 +0100 Subject: [PATCH 07/16] Bump com.android.tools:desugar_jdk_libs from 2.1.3 to 2.1.4 (#251) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 610f929b2..07b77d84e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ minSdk = "30" buildToolsVersion = "35.0.0" accompanist = "0.30.1" -androidDesugarJdkLibs = "2.1.3" +androidDesugarJdkLibs = "2.1.4" androidGradlePlugin = "8.7.1" androidxActivity = "1.9.3" androidxAppCompat = "1.7.0" From 41c5bd0799fce40089637affb9ef178ecb05291b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 11:19:37 +0100 Subject: [PATCH 08/16] Bump androidx.navigation:navigation-compose from 2.8.4 to 2.8.5 (#250) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 07b77d84e..c5e928134 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ androidxAppCompat = "1.7.0" androidxComposeBom = "2024.12.01" androidxDataStore = "1.1.1" androidxLifecycle = "2.8.7" -androidxNavigation = "2.8.4" +androidxNavigation = "2.8.5" androidxPaging = "3.3.5" androidxPagingCompose = "3.3.5" coil = "3.0.4" From de257a59404f5d72d372bf1f05877378f1b1efc8 Mon Sep 17 00:00:00 2001 From: Martin Felber <45291671+FelberMartin@users.noreply.github.com> Date: Sat, 21 Dec 2024 13:10:39 +0100 Subject: [PATCH 09/16] `Chore`: Update Material3 theme using "Color match" feature (#244) --- .../native_app/android/ui/theme/Color.kt | 265 ++++++++---------- .../native_app/android/ui/theme/Theme.kt | 60 +--- 2 files changed, 122 insertions(+), 203 deletions(-) diff --git a/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/theme/Color.kt b/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/theme/Color.kt index 6312fb315..0776a33a5 100644 --- a/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/theme/Color.kt +++ b/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/theme/Color.kt @@ -1,126 +1,127 @@ package de.tum.informatics.www1.artemis.native_app.android.ui.theme + import androidx.compose.ui.graphics.Color -val primaryLight = Color(0xFF2C638B) +val primaryLight = Color(0xFF005388) val onPrimaryLight = Color(0xFFFFFFFF) -val primaryContainerLight = Color(0xFFCCE5FF) -val onPrimaryContainerLight = Color(0xFF001D31) -val secondaryLight = Color(0xFF51606F) +val primaryContainerLight = Color(0xFF2779BA) +val onPrimaryContainerLight = Color(0xFFFFFFFF) +val secondaryLight = Color(0xFF48607B) val onSecondaryLight = Color(0xFFFFFFFF) -val secondaryContainerLight = Color(0xFFD4E4F6) -val onSecondaryContainerLight = Color(0xFF0D1D2A) -val tertiaryLight = Color(0xFF67587A) +val secondaryContainerLight = Color(0xFFCDE3FF) +val onSecondaryContainerLight = Color(0xFF314962) +val tertiaryLight = Color(0xFF713983) val onTertiaryLight = Color(0xFFFFFFFF) -val tertiaryContainerLight = Color(0xFFEDDCFF) -val onTertiaryContainerLight = Color(0xFF221533) +val tertiaryContainerLight = Color(0xFF995EAA) +val onTertiaryContainerLight = Color(0xFFFFFFFF) val errorLight = Color(0xFFBA1A1A) val onErrorLight = Color(0xFFFFFFFF) val errorContainerLight = Color(0xFFFFDAD6) val onErrorContainerLight = Color(0xFF410002) -val backgroundLight = Color(0xFFF7F9FF) -val onBackgroundLight = Color(0xFF181C20) -val surfaceLight = Color(0xFFF7F9FF) -val onSurfaceLight = Color(0xFF181C20) -val surfaceVariantLight = Color(0xFFDEE3EB) -val onSurfaceVariantLight = Color(0xFF42474E) -val outlineLight = Color(0xFF72787E) -val outlineVariantLight = Color(0xFFC2C7CE) +val backgroundLight = Color(0xFFF8F9FF) +val onBackgroundLight = Color(0xFF191C20) +val surfaceLight = Color(0xFFF8F9FF) +val onSurfaceLight = Color(0xFF191C20) +val surfaceVariantLight = Color(0xFFDCE3EE) +val onSurfaceVariantLight = Color(0xFF404750) +val outlineLight = Color(0xFF717881) +val outlineVariantLight = Color(0xFFC0C7D2) val scrimLight = Color(0xFF000000) val inverseSurfaceLight = Color(0xFF2D3135) -val inverseOnSurfaceLight = Color(0xFFEEF1F6) -val inversePrimaryLight = Color(0xFF99CCFA) -val surfaceDimLight = Color(0xFFD7DADF) -val surfaceBrightLight = Color(0xFFF7F9FF) +val inverseOnSurfaceLight = Color(0xFFEFF1F6) +val inversePrimaryLight = Color(0xFF9ACBFF) +val surfaceDimLight = Color(0xFFD8DAE0) +val surfaceBrightLight = Color(0xFFF8F9FF) val surfaceContainerLowestLight = Color(0xFFFFFFFF) -val surfaceContainerLowLight = Color(0xFFF1F4F9) -val surfaceContainerLight = Color(0xFFEBEEF3) +val surfaceContainerLowLight = Color(0xFFF2F3F9) +val surfaceContainerLight = Color(0xFFECEEF4) val surfaceContainerHighLight = Color(0xFFE6E8EE) val surfaceContainerHighestLight = Color(0xFFE0E2E8) -val primaryLightMediumContrast = Color(0xFF00476D) +val primaryLightMediumContrast = Color(0xFF004673) val onPrimaryLightMediumContrast = Color(0xFFFFFFFF) -val primaryContainerLightMediumContrast = Color(0xFF4579A3) +val primaryContainerLightMediumContrast = Color(0xFF2779BA) val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF) -val secondaryLightMediumContrast = Color(0xFF354453) +val secondaryLightMediumContrast = Color(0xFF2C455E) val onSecondaryLightMediumContrast = Color(0xFFFFFFFF) -val secondaryContainerLightMediumContrast = Color(0xFF677686) +val secondaryContainerLightMediumContrast = Color(0xFF5E7792) val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF) -val tertiaryLightMediumContrast = Color(0xFF4A3D5D) +val tertiaryLightMediumContrast = Color(0xFF622B74) val onTertiaryLightMediumContrast = Color(0xFFFFFFFF) -val tertiaryContainerLightMediumContrast = Color(0xFF7E6E92) +val tertiaryContainerLightMediumContrast = Color(0xFF995EAA) val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF) val errorLightMediumContrast = Color(0xFF8C0009) val onErrorLightMediumContrast = Color(0xFFFFFFFF) val errorContainerLightMediumContrast = Color(0xFFDA342E) val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF) -val backgroundLightMediumContrast = Color(0xFFF7F9FF) -val onBackgroundLightMediumContrast = Color(0xFF181C20) -val surfaceLightMediumContrast = Color(0xFFF7F9FF) -val onSurfaceLightMediumContrast = Color(0xFF181C20) -val surfaceVariantLightMediumContrast = Color(0xFFDEE3EB) -val onSurfaceVariantLightMediumContrast = Color(0xFF3E434A) -val outlineLightMediumContrast = Color(0xFF5A6066) -val outlineVariantLightMediumContrast = Color(0xFF767B82) +val backgroundLightMediumContrast = Color(0xFFF8F9FF) +val onBackgroundLightMediumContrast = Color(0xFF191C20) +val surfaceLightMediumContrast = Color(0xFFF8F9FF) +val onSurfaceLightMediumContrast = Color(0xFF191C20) +val surfaceVariantLightMediumContrast = Color(0xFFDCE3EE) +val onSurfaceVariantLightMediumContrast = Color(0xFF3D434C) +val outlineLightMediumContrast = Color(0xFF596069) +val outlineVariantLightMediumContrast = Color(0xFF747B85) val scrimLightMediumContrast = Color(0xFF000000) val inverseSurfaceLightMediumContrast = Color(0xFF2D3135) -val inverseOnSurfaceLightMediumContrast = Color(0xFFEEF1F6) -val inversePrimaryLightMediumContrast = Color(0xFF99CCFA) -val surfaceDimLightMediumContrast = Color(0xFFD7DADF) -val surfaceBrightLightMediumContrast = Color(0xFFF7F9FF) +val inverseOnSurfaceLightMediumContrast = Color(0xFFEFF1F6) +val inversePrimaryLightMediumContrast = Color(0xFF9ACBFF) +val surfaceDimLightMediumContrast = Color(0xFFD8DAE0) +val surfaceBrightLightMediumContrast = Color(0xFFF8F9FF) val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF) -val surfaceContainerLowLightMediumContrast = Color(0xFFF1F4F9) -val surfaceContainerLightMediumContrast = Color(0xFFEBEEF3) +val surfaceContainerLowLightMediumContrast = Color(0xFFF2F3F9) +val surfaceContainerLightMediumContrast = Color(0xFFECEEF4) val surfaceContainerHighLightMediumContrast = Color(0xFFE6E8EE) val surfaceContainerHighestLightMediumContrast = Color(0xFFE0E2E8) -val primaryLightHighContrast = Color(0xFF00243C) +val primaryLightHighContrast = Color(0xFF00243F) val onPrimaryLightHighContrast = Color(0xFFFFFFFF) -val primaryContainerLightHighContrast = Color(0xFF00476D) +val primaryContainerLightHighContrast = Color(0xFF004673) val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF) -val secondaryLightHighContrast = Color(0xFF142431) +val secondaryLightHighContrast = Color(0xFF07243B) val onSecondaryLightHighContrast = Color(0xFFFFFFFF) -val secondaryContainerLightHighContrast = Color(0xFF354453) +val secondaryContainerLightHighContrast = Color(0xFF2C455E) val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF) -val tertiaryLightHighContrast = Color(0xFF291C3B) +val tertiaryLightHighContrast = Color(0xFF3D0150) val onTertiaryLightHighContrast = Color(0xFFFFFFFF) -val tertiaryContainerLightHighContrast = Color(0xFF4A3D5D) +val tertiaryContainerLightHighContrast = Color(0xFF622B74) val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF) val errorLightHighContrast = Color(0xFF4E0002) val onErrorLightHighContrast = Color(0xFFFFFFFF) val errorContainerLightHighContrast = Color(0xFF8C0009) val onErrorContainerLightHighContrast = Color(0xFFFFFFFF) -val backgroundLightHighContrast = Color(0xFFF7F9FF) -val onBackgroundLightHighContrast = Color(0xFF181C20) -val surfaceLightHighContrast = Color(0xFFF7F9FF) +val backgroundLightHighContrast = Color(0xFFF8F9FF) +val onBackgroundLightHighContrast = Color(0xFF191C20) +val surfaceLightHighContrast = Color(0xFFF8F9FF) val onSurfaceLightHighContrast = Color(0xFF000000) -val surfaceVariantLightHighContrast = Color(0xFFDEE3EB) -val onSurfaceVariantLightHighContrast = Color(0xFF1F242A) -val outlineLightHighContrast = Color(0xFF3E434A) -val outlineVariantLightHighContrast = Color(0xFF3E434A) +val surfaceVariantLightHighContrast = Color(0xFFDCE3EE) +val onSurfaceVariantLightHighContrast = Color(0xFF1E242C) +val outlineLightHighContrast = Color(0xFF3D434C) +val outlineVariantLightHighContrast = Color(0xFF3D434C) val scrimLightHighContrast = Color(0xFF000000) val inverseSurfaceLightHighContrast = Color(0xFF2D3135) val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF) -val inversePrimaryLightHighContrast = Color(0xFFDFEEFF) -val surfaceDimLightHighContrast = Color(0xFFD7DADF) -val surfaceBrightLightHighContrast = Color(0xFFF7F9FF) +val inversePrimaryLightHighContrast = Color(0xFFE1EDFF) +val surfaceDimLightHighContrast = Color(0xFFD8DAE0) +val surfaceBrightLightHighContrast = Color(0xFFF8F9FF) val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF) -val surfaceContainerLowLightHighContrast = Color(0xFFF1F4F9) -val surfaceContainerLightHighContrast = Color(0xFFEBEEF3) +val surfaceContainerLowLightHighContrast = Color(0xFFF2F3F9) +val surfaceContainerLightHighContrast = Color(0xFFECEEF4) val surfaceContainerHighLightHighContrast = Color(0xFFE6E8EE) val surfaceContainerHighestLightHighContrast = Color(0xFFE0E2E8) -val primaryDark = Color(0xFF99CCFA) -val onPrimaryDark = Color(0xFF003351) -val primaryContainerDark = Color(0xFF084B72) -val onPrimaryContainerDark = Color(0xFFCCE5FF) -val secondaryDark = Color(0xFFB8C8DA) -val onSecondaryDark = Color(0xFF23323F) -val secondaryContainerDark = Color(0xFF394857) -val onSecondaryContainerDark = Color(0xFFD4E4F6) -val tertiaryDark = Color(0xFFD2BFE7) -val onTertiaryDark = Color(0xFF372A4A) -val tertiaryContainerDark = Color(0xFF4E4161) -val onTertiaryContainerDark = Color(0xFFEDDCFF) +val primaryDark = Color(0xFF9ACBFF) +val onPrimaryDark = Color(0xFF003355) +val primaryContainerDark = Color(0xFF2779BA) +val onPrimaryContainerDark = Color(0xFFFFFFFF) +val secondaryDark = Color(0xFFB0C9E7) +val onSecondaryDark = Color(0xFF18324A) +val secondaryContainerDark = Color(0xFF263F58) +val onSecondaryContainerDark = Color(0xFFBAD3F2) +val tertiaryDark = Color(0xFFF0B0FF) +val onTertiaryDark = Color(0xFF4D1560) +val tertiaryContainerDark = Color(0xFF995EAA) +val onTertiaryContainerDark = Color(0xFFFFFFFF) val errorDark = Color(0xFFFFB4AB) val onErrorDark = Color(0xFF690005) val errorContainerDark = Color(0xFF93000A) @@ -129,33 +130,33 @@ val backgroundDark = Color(0xFF101418) val onBackgroundDark = Color(0xFFE0E2E8) val surfaceDark = Color(0xFF101418) val onSurfaceDark = Color(0xFFE0E2E8) -val surfaceVariantDark = Color(0xFF42474E) -val onSurfaceVariantDark = Color(0xFFC2C7CE) -val outlineDark = Color(0xFF8C9198) -val outlineVariantDark = Color(0xFF42474E) +val surfaceVariantDark = Color(0xFF404750) +val onSurfaceVariantDark = Color(0xFFC0C7D2) +val outlineDark = Color(0xFF8A919B) +val outlineVariantDark = Color(0xFF404750) val scrimDark = Color(0xFF000000) val inverseSurfaceDark = Color(0xFFE0E2E8) val inverseOnSurfaceDark = Color(0xFF2D3135) -val inversePrimaryDark = Color(0xFF2C638B) +val inversePrimaryDark = Color(0xFF00629F) val surfaceDimDark = Color(0xFF101418) val surfaceBrightDark = Color(0xFF36393E) -val surfaceContainerLowestDark = Color(0xFF0B0F12) -val surfaceContainerLowDark = Color(0xFF181C20) -val surfaceContainerDark = Color(0xFF1C2024) -val surfaceContainerHighDark = Color(0xFF272A2E) -val surfaceContainerHighestDark = Color(0xFF313539) +val surfaceContainerLowestDark = Color(0xFF0B0E12) +val surfaceContainerLowDark = Color(0xFF191C20) +val surfaceContainerDark = Color(0xFF1D2024) +val surfaceContainerHighDark = Color(0xFF272A2F) +val surfaceContainerHighestDark = Color(0xFF32353A) -val primaryDarkMediumContrast = Color(0xFF9DD0FE) -val onPrimaryDarkMediumContrast = Color(0xFF001829) -val primaryContainerDarkMediumContrast = Color(0xFF6395C1) +val primaryDarkMediumContrast = Color(0xFFA3CFFF) +val onPrimaryDarkMediumContrast = Color(0xFF00182C) +val primaryContainerDarkMediumContrast = Color(0xFF4C95D8) val onPrimaryContainerDarkMediumContrast = Color(0xFF000000) -val secondaryDarkMediumContrast = Color(0xFFBDCCDE) -val onSecondaryDarkMediumContrast = Color(0xFF071824) -val secondaryContainerDarkMediumContrast = Color(0xFF8392A3) +val secondaryDarkMediumContrast = Color(0xFFB4CDEB) +val onSecondaryDarkMediumContrast = Color(0xFF00182C) +val secondaryContainerDarkMediumContrast = Color(0xFF7A93AF) val onSecondaryContainerDarkMediumContrast = Color(0xFF000000) -val tertiaryDarkMediumContrast = Color(0xFFD6C3EB) -val onTertiaryDarkMediumContrast = Color(0xFF1C102E) -val tertiaryContainerDarkMediumContrast = Color(0xFF9A8AAF) +val tertiaryDarkMediumContrast = Color(0xFFF1B6FF) +val onTertiaryDarkMediumContrast = Color(0xFF2B003A) +val tertiaryContainerDarkMediumContrast = Color(0xFFB879C8) val onTertiaryContainerDarkMediumContrast = Color(0xFF000000) val errorDarkMediumContrast = Color(0xFFFFBAB1) val onErrorDarkMediumContrast = Color(0xFF370001) @@ -164,34 +165,34 @@ val onErrorContainerDarkMediumContrast = Color(0xFF000000) val backgroundDarkMediumContrast = Color(0xFF101418) val onBackgroundDarkMediumContrast = Color(0xFFE0E2E8) val surfaceDarkMediumContrast = Color(0xFF101418) -val onSurfaceDarkMediumContrast = Color(0xFFF9FBFF) -val surfaceVariantDarkMediumContrast = Color(0xFF42474E) -val onSurfaceVariantDarkMediumContrast = Color(0xFFC6CBD3) -val outlineDarkMediumContrast = Color(0xFF9EA3AB) -val outlineVariantDarkMediumContrast = Color(0xFF7E848B) +val onSurfaceDarkMediumContrast = Color(0xFFFAFAFF) +val surfaceVariantDarkMediumContrast = Color(0xFF404750) +val onSurfaceVariantDarkMediumContrast = Color(0xFFC5CBD6) +val outlineDarkMediumContrast = Color(0xFF9DA3AE) +val outlineVariantDarkMediumContrast = Color(0xFF7D848D) val scrimDarkMediumContrast = Color(0xFF000000) val inverseSurfaceDarkMediumContrast = Color(0xFFE0E2E8) -val inverseOnSurfaceDarkMediumContrast = Color(0xFF272A2E) -val inversePrimaryDarkMediumContrast = Color(0xFF0A4C73) +val inverseOnSurfaceDarkMediumContrast = Color(0xFF272A2F) +val inversePrimaryDarkMediumContrast = Color(0xFF004B7B) val surfaceDimDarkMediumContrast = Color(0xFF101418) val surfaceBrightDarkMediumContrast = Color(0xFF36393E) -val surfaceContainerLowestDarkMediumContrast = Color(0xFF0B0F12) -val surfaceContainerLowDarkMediumContrast = Color(0xFF181C20) -val surfaceContainerDarkMediumContrast = Color(0xFF1C2024) -val surfaceContainerHighDarkMediumContrast = Color(0xFF272A2E) -val surfaceContainerHighestDarkMediumContrast = Color(0xFF313539) +val surfaceContainerLowestDarkMediumContrast = Color(0xFF0B0E12) +val surfaceContainerLowDarkMediumContrast = Color(0xFF191C20) +val surfaceContainerDarkMediumContrast = Color(0xFF1D2024) +val surfaceContainerHighDarkMediumContrast = Color(0xFF272A2F) +val surfaceContainerHighestDarkMediumContrast = Color(0xFF32353A) -val primaryDarkHighContrast = Color(0xFFF9FBFF) +val primaryDarkHighContrast = Color(0xFFFAFAFF) val onPrimaryDarkHighContrast = Color(0xFF000000) -val primaryContainerDarkHighContrast = Color(0xFF9DD0FE) +val primaryContainerDarkHighContrast = Color(0xFFA3CFFF) val onPrimaryContainerDarkHighContrast = Color(0xFF000000) -val secondaryDarkHighContrast = Color(0xFFF9FBFF) +val secondaryDarkHighContrast = Color(0xFFFAFAFF) val onSecondaryDarkHighContrast = Color(0xFF000000) -val secondaryContainerDarkHighContrast = Color(0xFFBDCCDE) +val secondaryContainerDarkHighContrast = Color(0xFFB4CDEB) val onSecondaryContainerDarkHighContrast = Color(0xFF000000) -val tertiaryDarkHighContrast = Color(0xFFFFF9FD) +val tertiaryDarkHighContrast = Color(0xFFFFF9FA) val onTertiaryDarkHighContrast = Color(0xFF000000) -val tertiaryContainerDarkHighContrast = Color(0xFFD6C3EB) +val tertiaryContainerDarkHighContrast = Color(0xFFF1B6FF) val onTertiaryContainerDarkHighContrast = Color(0xFF000000) val errorDarkHighContrast = Color(0xFFFFF9F9) val onErrorDarkHighContrast = Color(0xFF000000) @@ -201,49 +202,25 @@ val backgroundDarkHighContrast = Color(0xFF101418) val onBackgroundDarkHighContrast = Color(0xFFE0E2E8) val surfaceDarkHighContrast = Color(0xFF101418) val onSurfaceDarkHighContrast = Color(0xFFFFFFFF) -val surfaceVariantDarkHighContrast = Color(0xFF42474E) -val onSurfaceVariantDarkHighContrast = Color(0xFFF9FBFF) -val outlineDarkHighContrast = Color(0xFFC6CBD3) -val outlineVariantDarkHighContrast = Color(0xFFC6CBD3) +val surfaceVariantDarkHighContrast = Color(0xFF404750) +val onSurfaceVariantDarkHighContrast = Color(0xFFFAFAFF) +val outlineDarkHighContrast = Color(0xFFC5CBD6) +val outlineVariantDarkHighContrast = Color(0xFFC5CBD6) val scrimDarkHighContrast = Color(0xFF000000) val inverseSurfaceDarkHighContrast = Color(0xFFE0E2E8) val inverseOnSurfaceDarkHighContrast = Color(0xFF000000) -val inversePrimaryDarkHighContrast = Color(0xFF002D47) +val inversePrimaryDarkHighContrast = Color(0xFF002C4B) val surfaceDimDarkHighContrast = Color(0xFF101418) val surfaceBrightDarkHighContrast = Color(0xFF36393E) -val surfaceContainerLowestDarkHighContrast = Color(0xFF0B0F12) -val surfaceContainerLowDarkHighContrast = Color(0xFF181C20) -val surfaceContainerDarkHighContrast = Color(0xFF1C2024) -val surfaceContainerHighDarkHighContrast = Color(0xFF272A2E) -val surfaceContainerHighestDarkHighContrast = Color(0xFF313539) +val surfaceContainerLowestDarkHighContrast = Color(0xFF0B0E12) +val surfaceContainerLowDarkHighContrast = Color(0xFF191C20) +val surfaceContainerDarkHighContrast = Color(0xFF1D2024) +val surfaceContainerHighDarkHighContrast = Color(0xFF272A2F) +val surfaceContainerHighestDarkHighContrast = Color(0xFF32353A) -val customColorLight = Color(0xFF3A608F) -val onCustomColorLight = Color(0xFFFFFFFF) -val customColorContainerLight = Color(0xFFD3E3FF) -val onCustomColorContainerLight = Color(0xFF001C39) -val customColorLightMediumContrast = Color(0xFF1B4472) -val onCustomColorLightMediumContrast = Color(0xFFFFFFFF) -val customColorContainerLightMediumContrast = Color(0xFF5276A7) -val onCustomColorContainerLightMediumContrast = Color(0xFFFFFFFF) -val customColorLightHighContrast = Color(0xFF002344) -val onCustomColorLightHighContrast = Color(0xFFFFFFFF) -val customColorContainerLightHighContrast = Color(0xFF1B4472) -val onCustomColorContainerLightHighContrast = Color(0xFFFFFFFF) -val customColorDark = Color(0xFFA4C9FE) -val onCustomColorDark = Color(0xFF00315D) -val customColorContainerDark = Color(0xFF204876) -val onCustomColorContainerDark = Color(0xFFD3E3FF) -val customColorDarkMediumContrast = Color(0xFFABCDFF) -val onCustomColorDarkMediumContrast = Color(0xFF001730) -val customColorContainerDarkMediumContrast = Color(0xFF6E93C5) -val onCustomColorContainerDarkMediumContrast = Color(0xFF000000) -val customColorDarkHighContrast = Color(0xFFFBFAFF) -val onCustomColorDarkHighContrast = Color(0xFF000000) -val customColorContainerDarkHighContrast = Color(0xFFABCDFF) -val onCustomColorContainerDarkHighContrast = Color(0xFF000000) diff --git a/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/theme/Theme.kt b/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/theme/Theme.kt index 403900ea7..a9e05f754 100644 --- a/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/theme/Theme.kt +++ b/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/theme/Theme.kt @@ -1,4 +1,5 @@ package de.tum.informatics.www1.artemis.native_app.android.ui.theme + import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme @@ -11,11 +12,6 @@ import androidx.compose.runtime.Immutable import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -@Immutable -data class ExtendedColorScheme( - val customColor: ColorFamily, -) - private val lightScheme = lightColorScheme( primary = primaryLight, onPrimary = onPrimaryLight, @@ -244,60 +240,6 @@ private val highContrastDarkColorScheme = darkColorScheme( surfaceContainerHighest = surfaceContainerHighestDarkHighContrast, ) -val extendedLight = ExtendedColorScheme( - customColor = ColorFamily( - customColorLight, - onCustomColorLight, - customColorContainerLight, - onCustomColorContainerLight, - ), -) - -val extendedDark = ExtendedColorScheme( - customColor = ColorFamily( - customColorDark, - onCustomColorDark, - customColorContainerDark, - onCustomColorContainerDark, - ), -) - -val extendedLightMediumContrast = ExtendedColorScheme( - customColor = ColorFamily( - customColorLightMediumContrast, - onCustomColorLightMediumContrast, - customColorContainerLightMediumContrast, - onCustomColorContainerLightMediumContrast, - ), -) - -val extendedLightHighContrast = ExtendedColorScheme( - customColor = ColorFamily( - customColorLightHighContrast, - onCustomColorLightHighContrast, - customColorContainerLightHighContrast, - onCustomColorContainerLightHighContrast, - ), -) - -val extendedDarkMediumContrast = ExtendedColorScheme( - customColor = ColorFamily( - customColorDarkMediumContrast, - onCustomColorDarkMediumContrast, - customColorContainerDarkMediumContrast, - onCustomColorContainerDarkMediumContrast, - ), -) - -val extendedDarkHighContrast = ExtendedColorScheme( - customColor = ColorFamily( - customColorDarkHighContrast, - onCustomColorDarkHighContrast, - customColorContainerDarkHighContrast, - onCustomColorContainerDarkHighContrast, - ), -) - @Immutable data class ColorFamily( val color: Color, From 6379d30d8cdf6132aa72bbf63aaacc8b3f14f595 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 13:16:06 +0100 Subject: [PATCH 10/16] Bump ktor from 3.0.2 to 3.0.3 (#249) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Felber --- .../www1/artemis/native_app/feature/quiz/QuizBaseE2eTest.kt | 2 +- gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/quiz/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/QuizBaseE2eTest.kt b/feature/quiz/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/QuizBaseE2eTest.kt index 3276f1aee..d68b67791 100644 --- a/feature/quiz/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/QuizBaseE2eTest.kt +++ b/feature/quiz/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/QuizBaseE2eTest.kt @@ -50,7 +50,7 @@ internal abstract class QuizBaseE2eTest(protected val quizType: QuizType.Workabl fun setup() { ShadowLog.stream = System.out - runBlockingWithTestTimeout { + runBlockingWithTestTimeout(timeoutMultiplier = 2) { course = createCourse(getAdminAccessToken()) courseId = course.id!! diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c5e928134..57fe11437 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ koin = "4.0.0" koinAndroidxCompose = "4.0.0" kover = "0.9.0" krossbow = "8.1.0" -ktor = "3.0.2" +ktor = "3.0.3" markwon = "4.6.2" mockk = "1.13.14" ossLicensesPlugin = "0.10.6" From dc3006de1a1365455d46349f6de5b0d649ad3063 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 14:26:11 +0100 Subject: [PATCH 11/16] Bump kotlinxCoroutines from 1.9.0 to 1.10.1 (#248) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Felber <45291671+FelberMartin@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 57fe11437..1be1b6727 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ coil = "3.0.4" coil2 = "2.7.0" # The markwon library still depends on coil2: https://github.com/ls1intum/artemis-android/issues/171 emoji2 = "1.5.0" kotlin = "2.1.0" -kotlinxCoroutines = "1.9.0" +kotlinxCoroutines = "1.10.1" kotlinxDatetime = "0.6.1" kotlinxSerializationJson = "1.7.3" koin = "4.0.0" From 2a2dcac5df14f13641bc98dadfe9ac15db0fe4c9 Mon Sep 17 00:00:00 2001 From: Martin Felber <45291671+FelberMartin@users.noreply.github.com> Date: Sat, 21 Dec 2024 14:32:52 +0100 Subject: [PATCH 12/16] `Bugfix`: Fix crash when updating post with same clientPostId (#242) --- .../storage/impl/MetisStorageServiceImpl.kt | 8 ++--- .../impl/MetisStorageServiceTestUpdatePost.kt | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageServiceTestUpdatePost.kt diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageServiceImpl.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageServiceImpl.kt index 63883a971..3763185c1 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageServiceImpl.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageServiceImpl.kt @@ -148,13 +148,13 @@ internal class MetisStorageServiceImpl( serverId: String, clientSidePostId: String, serverSidePostId: Long?, - conversationId: Long = this.conversationId, + conversationId: Long? = null, postingType: BasePostingEntity.PostingType ): MetisPostContextEntity = MetisPostContextEntity( serverId = serverId, courseId = courseId, - conversationId = conversationId, + conversationId = conversationId ?: this.conversationId, serverPostId = serverSidePostId, clientPostId = clientSidePostId, postingType = postingType @@ -483,7 +483,7 @@ internal class MetisStorageServiceImpl( serverId = host, clientSidePostId = clientSidePostId, serverSidePostId = standalonePostId, - conversationId = conversationId?: metisContext.conversationId, + conversationId = conversationId, postingType = BasePostingEntity.PostingType.STANDALONE ) @@ -501,7 +501,7 @@ internal class MetisStorageServiceImpl( clientSidePostId, standalonePostId, courseId = postMetisContext.courseId, - conversationId = metisContext.conversationId + conversationId = postMetisContext.conversationId ) if (!isPostPresent) { diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageServiceTestUpdatePost.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageServiceTestUpdatePost.kt new file mode 100644 index 000000000..abb59ae93 --- /dev/null +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/service/storage/impl/MetisStorageServiceTestUpdatePost.kt @@ -0,0 +1,35 @@ +package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.service.storage.impl + +import de.tum.informatics.www1.artemis.native_app.core.common.test.UnitTest +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.experimental.categories.Category +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@Category(UnitTest::class) +@RunWith(RobolectricTestRunner::class) +class MetisStorageServiceTestUpdatePost : MetisStorageBaseTest() { + + @Test + fun testUpdatePostWithSameClientPostId() = runTest { + // GIVEN: Two posts in two different conversations with the same clientPostId + val post1 = basePost + val post2 = basePostTwo + sut.insertOrUpdatePosts( + host = host, + metisContext = metisContext, + posts = listOf(post1, post2), + ) + + // WHEN: Updating post1 while the MetisContext of post2 is active + sut.updatePost( + host = host, + metisContext = metisContextTwo, + post = post1 + ) + + // THEN: The previous call should not crash and this assertion should be reached + assert(true) + } +} \ No newline at end of file From ce3b4d8cff2eba5cb595e54353e87741047b3928 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2024 17:42:39 +0100 Subject: [PATCH 13/16] Bump sentry-android from 7.19.0 to 7.19.1 (#256) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1be1b6727..8353c3225 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ mockk = "1.13.14" ossLicensesPlugin = "0.10.6" placeholderMaterial = "2.0.0" room = "2.6.1" -sentry-android = "7.19.0" +sentry-android = "7.19.1" work = "2.10.0" [libraries] From 8490b0ba6558bd915c3f0370d71fec374f17de41 Mon Sep 17 00:00:00 2001 From: Martin Felber <45291671+FelberMartin@users.noreply.github.com> Date: Fri, 27 Dec 2024 18:09:17 +0100 Subject: [PATCH 14/16] `Feature`: Add navigation animations (#222) --- .../core/ui/navigation/DefaultTransition.kt | 57 ++++ .../ui/navigation/navigation_animation.kt | 46 +++ .../courseregistration/RegisterForCourseUi.kt | 4 +- .../ui/course_overview/CourseUiScreen.kt | 25 +- .../feature/dashboard/ui/CoursesOverview.kt | 4 +- .../feature/exerciseview/ExerciseViewUi.kt | 10 +- .../feature/lectureview/LectureScreenUi.kt | 8 +- .../native_app/feature/login/AccountUi.kt | 81 ++--- .../InstanceSelectionScreen.kt | 71 +---- .../conversation/ui/ConversationScreen.kt | 89 +++--- .../metis/ConversationConfiguration.kt | 59 ++++ .../feature/metis/ui/ConversationFacadeUi.kt | 2 + .../metis/ui/SinglePageConversationBody.kt | 278 ++++++++---------- .../participation/QuizParticipationScreen.kt | 4 +- .../quiz/view_result/ViewQuizResultScreen.kt | 4 +- feature/settings/build.gradle.kts | 1 + .../feature/settings/SettingsScreen.kt | 16 +- 17 files changed, 450 insertions(+), 309 deletions(-) create mode 100644 core/ui/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/ui/navigation/DefaultTransition.kt create mode 100644 core/ui/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/ui/navigation/navigation_animation.kt create mode 100644 feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ConversationConfiguration.kt diff --git a/core/ui/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/ui/navigation/DefaultTransition.kt b/core/ui/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/ui/navigation/DefaultTransition.kt new file mode 100644 index 000000000..300bedd6b --- /dev/null +++ b/core/ui/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/ui/navigation/DefaultTransition.kt @@ -0,0 +1,57 @@ +package de.tum.informatics.www1.artemis.native_app.core.ui.navigation + +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInHorizontally +import androidx.compose.animation.slideOutHorizontally +import androidx.compose.animation.togetherWith + + +object DefaultTransition { + const val duration = 220 + + val fadeIn = fadeIn(tween(duration)) + val fadeOut = fadeOut(tween(duration)) + + val navigateForward = enter togetherWith exit + val navigateBack = popEnter togetherWith popExit + val navigateNeutral = fadeIn togetherWith fadeOut + + val enter + get() = slideIn(AnimatedContentTransitionScope.SlideDirection.Left) + + val exit + get() = slideOut(AnimatedContentTransitionScope.SlideDirection.Left) + + val popEnter + get() = slideIn(AnimatedContentTransitionScope.SlideDirection.Right) + + val popExit + get() = slideOut(AnimatedContentTransitionScope.SlideDirection.Right) + + fun slideIn( + direction: AnimatedContentTransitionScope.SlideDirection + ): EnterTransition = slideInHorizontally( + animationSpec = tween(duration) + ) { width -> + return@slideInHorizontally when(direction) { + AnimatedContentTransitionScope.SlideDirection.Left -> width + else -> - width + } + } + fadeIn + + fun slideOut( + direction: AnimatedContentTransitionScope.SlideDirection + ): ExitTransition = slideOutHorizontally( + animationSpec = tween(duration) + ) { width -> + return@slideOutHorizontally when(direction) { + AnimatedContentTransitionScope.SlideDirection.Left -> - width + else -> width + } + } + fadeOut +} diff --git a/core/ui/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/ui/navigation/navigation_animation.kt b/core/ui/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/ui/navigation/navigation_animation.kt new file mode 100644 index 000000000..d31341a43 --- /dev/null +++ b/core/ui/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/core/ui/navigation/navigation_animation.kt @@ -0,0 +1,46 @@ +package de.tum.informatics.www1.artemis.native_app.core.ui.navigation + +import androidx.compose.animation.AnimatedContentScope +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.EnterTransition +import androidx.compose.animation.ExitTransition +import androidx.compose.animation.SizeTransform +import androidx.compose.runtime.Composable +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavDeepLink +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavType +import androidx.navigation.compose.composable +import kotlin.reflect.KType + + +inline fun NavGraphBuilder.animatedComposable( + typeMap: Map> = emptyMap(), + deepLinks: List = emptyList(), + noinline enterTransition: + (AnimatedContentTransitionScope.() -> @JvmSuppressWildcards + EnterTransition?)? = { DefaultTransition.enter }, + noinline exitTransition: + (AnimatedContentTransitionScope.() -> @JvmSuppressWildcards + ExitTransition?)? = { DefaultTransition.exit }, + noinline popEnterTransition: + (AnimatedContentTransitionScope.() -> @JvmSuppressWildcards + EnterTransition?)? = { DefaultTransition.popEnter }, + noinline popExitTransition: + (AnimatedContentTransitionScope.() -> @JvmSuppressWildcards + ExitTransition?)? = { DefaultTransition.popExit }, + noinline sizeTransform: + (AnimatedContentTransitionScope.() -> @JvmSuppressWildcards + SizeTransform?)? = + null, + noinline content: @Composable AnimatedContentScope.(NavBackStackEntry) -> Unit +) = composable( + enterTransition = enterTransition, + exitTransition = exitTransition, + popEnterTransition = popEnterTransition, + popExitTransition = popExitTransition, + sizeTransform = sizeTransform, + typeMap = typeMap, + deepLinks = deepLinks, + content = content +) diff --git a/feature/course-registration/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/courseregistration/RegisterForCourseUi.kt b/feature/course-registration/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/courseregistration/RegisterForCourseUi.kt index 032e0691a..94a1be956 100644 --- a/feature/course-registration/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/courseregistration/RegisterForCourseUi.kt +++ b/feature/course-registration/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/courseregistration/RegisterForCourseUi.kt @@ -51,7 +51,6 @@ import androidx.compose.ui.unit.sp import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder -import androidx.navigation.compose.composable import de.tum.informatics.www1.artemis.native_app.core.data.DataState import de.tum.informatics.www1.artemis.native_app.core.model.Course import de.tum.informatics.www1.artemis.native_app.core.ui.AwaitDeferredCompletion @@ -63,6 +62,7 @@ import de.tum.informatics.www1.artemis.native_app.core.ui.common.course.computeC import de.tum.informatics.www1.artemis.native_app.core.ui.common.course.computeCourseItemModifier import de.tum.informatics.www1.artemis.native_app.core.ui.getWindowSizeClass import de.tum.informatics.www1.artemis.native_app.core.ui.markdown.MarkdownText +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable import kotlinx.coroutines.Deferred import kotlinx.serialization.Serializable import org.koin.androidx.compose.koinViewModel @@ -82,7 +82,7 @@ fun NavGraphBuilder.courseRegistration( onNavigateUp: () -> Unit, onRegisteredInCourse: (courseId: Long) -> Unit ) { - composable { + animatedComposable { RegisterForCourseScreen( modifier = Modifier.fillMaxSize(), viewModel = koinViewModel(), diff --git a/feature/course-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/courseview/ui/course_overview/CourseUiScreen.kt b/feature/course-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/courseview/ui/course_overview/CourseUiScreen.kt index 29d957e69..6ba43dce9 100644 --- a/feature/course-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/courseview/ui/course_overview/CourseUiScreen.kt +++ b/feature/course-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/courseview/ui/course_overview/CourseUiScreen.kt @@ -2,9 +2,6 @@ package de.tum.informatics.www1.artemis.native_app.feature.courseview.ui.course_ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.SizeTransform -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally -import androidx.compose.animation.togetherWith import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.consumeWindowInsets @@ -20,7 +17,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -33,9 +29,6 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder -import androidx.navigation.NavType -import androidx.navigation.compose.composable -import androidx.navigation.navArgument import androidx.navigation.navDeepLink import androidx.navigation.toRoute import de.tum.informatics.www1.artemis.native_app.core.data.DataState @@ -46,17 +39,19 @@ import de.tum.informatics.www1.artemis.native_app.core.ui.common.BasicDataStateU import de.tum.informatics.www1.artemis.native_app.core.ui.common.EmptyDataStateUi import de.tum.informatics.www1.artemis.native_app.core.ui.exercise.BoundExerciseActions import de.tum.informatics.www1.artemis.native_app.core.ui.generateLinks +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.DefaultTransition +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable import de.tum.informatics.www1.artemis.native_app.feature.courseview.GroupedByWeek import de.tum.informatics.www1.artemis.native_app.feature.courseview.R import de.tum.informatics.www1.artemis.native_app.feature.courseview.ui.CourseViewModel import de.tum.informatics.www1.artemis.native_app.feature.courseview.ui.LectureListUi import de.tum.informatics.www1.artemis.native_app.feature.courseview.ui.exercise_list.ExerciseListUi +import de.tum.informatics.www1.artemis.native_app.feature.metis.NavigateToUserConversation +import de.tum.informatics.www1.artemis.native_app.feature.metis.NothingOpened +import de.tum.informatics.www1.artemis.native_app.feature.metis.OpenedConversation +import de.tum.informatics.www1.artemis.native_app.feature.metis.OpenedThread import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.StandalonePostId import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.ConversationFacadeUi -import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.NavigateToUserConversation -import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.NothingOpened -import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.OpenedConversation -import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.OpenedThread import kotlinx.serialization.Serializable import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf @@ -98,7 +93,7 @@ fun NavGraphBuilder.course( generateLinks("courses/{courseId}/exercises") + generateLinks("courses/{courseId}/messages?conversationId={conversationId}") + generateLinks("courses/{courseId}/messages?username={username}") - composable( + animatedComposable( deepLinks = deepLinks ) { backStackEntry -> val route: CourseUiScreen = backStackEntry.toRoute() @@ -346,11 +341,9 @@ internal fun CourseUiScreen( targetState = selectedTabIndex, transitionSpec = { if (targetState > initialState) { - slideInHorizontally { width -> width } togetherWith - slideOutHorizontally { width -> -width } + DefaultTransition.navigateForward } else { - slideInHorizontally { width -> -width } togetherWith - slideOutHorizontally { width -> width } + DefaultTransition.navigateBack }.using( SizeTransform(clip = false) ) diff --git a/feature/dashboard/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/dashboard/ui/CoursesOverview.kt b/feature/dashboard/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/dashboard/ui/CoursesOverview.kt index 77f94ba77..d824d68e6 100644 --- a/feature/dashboard/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/dashboard/ui/CoursesOverview.kt +++ b/feature/dashboard/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/dashboard/ui/CoursesOverview.kt @@ -43,9 +43,9 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder -import androidx.navigation.compose.composable import de.tum.informatics.www1.artemis.native_app.core.model.Dashboard import de.tum.informatics.www1.artemis.native_app.core.ui.common.BasicDataStateUi +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable import de.tum.informatics.www1.artemis.native_app.feature.dashboard.BuildConfig import de.tum.informatics.www1.artemis.native_app.feature.dashboard.R import de.tum.informatics.www1.artemis.native_app.feature.dashboard.service.BetaHintService @@ -71,7 +71,7 @@ fun NavGraphBuilder.dashboard( onClickRegisterForCourse: () -> Unit, onViewCourse: (courseId: Long) -> Unit ) { - composable { + animatedComposable { CoursesOverview( modifier = Modifier.fillMaxSize(), viewModel = koinViewModel(), diff --git a/feature/exercise-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/exerciseview/ExerciseViewUi.kt b/feature/exercise-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/exerciseview/ExerciseViewUi.kt index bd5798234..4cc6c0c36 100644 --- a/feature/exercise-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/exerciseview/ExerciseViewUi.kt +++ b/feature/exercise-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/exerciseview/ExerciseViewUi.kt @@ -14,7 +14,6 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navDeepLink import androidx.navigation.toRoute @@ -25,6 +24,7 @@ import de.tum.informatics.www1.artemis.native_app.core.ui.common.BasicDataStateU import de.tum.informatics.www1.artemis.native_app.core.ui.common.EmptyDataStateUi import de.tum.informatics.www1.artemis.native_app.core.ui.generateLinks import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.KSerializableNavType +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable import de.tum.informatics.www1.artemis.native_app.feature.exerciseview.home.ExerciseScreen import de.tum.informatics.www1.artemis.native_app.feature.exerciseview.participate.textexercise.TextExerciseParticipationScreen import de.tum.informatics.www1.artemis.native_app.feature.exerciseview.viewresult.ViewResultScreen @@ -82,7 +82,7 @@ fun NavGraphBuilder.exercise( onParticipateInQuiz: (courseId: Long, exerciseId: Long, isPractice: Boolean) -> Unit, onClickViewQuizResults: (courseId: Long, exerciseId: Long) -> Unit ) { - composable( + animatedComposable( typeMap = mapOf( typeOf() to KSerializableNavType( isNullableAllowed = false, @@ -131,7 +131,7 @@ fun NavGraphBuilder.exercise( } NavHost(navController = nestedNavController, startDestination = startDestination) { - composable { + animatedComposable { ExerciseScreen( modifier = Modifier.fillMaxSize(), viewModel = exerciseViewModel, @@ -156,7 +156,7 @@ fun NavGraphBuilder.exercise( ) } - composable { + animatedComposable { ViewResultScreen( modifier = Modifier.fillMaxSize(), viewModel = exerciseViewModel, @@ -164,7 +164,7 @@ fun NavGraphBuilder.exercise( ) } - composable { backStackEntry -> + animatedComposable { backStackEntry -> val nestedRoute: ExerciseViewUiNestedNavigation.ParticipateTextExercise = backStackEntry.toRoute() diff --git a/feature/lecture-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/lectureview/LectureScreenUi.kt b/feature/lecture-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/lectureview/LectureScreenUi.kt index e4884dab0..cf461b59d 100644 --- a/feature/lecture-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/lectureview/LectureScreenUi.kt +++ b/feature/lecture-view/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/lectureview/LectureScreenUi.kt @@ -31,18 +31,16 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder -import androidx.navigation.NavType -import androidx.navigation.compose.composable -import androidx.navigation.navArgument import androidx.navigation.navDeepLink import androidx.navigation.toRoute -import io.github.fornewid.placeholder.material3.placeholder import de.tum.informatics.www1.artemis.native_app.core.model.lecture.Attachment import de.tum.informatics.www1.artemis.native_app.core.ui.LocalLinkOpener import de.tum.informatics.www1.artemis.native_app.core.ui.alert.TextAlertDialog import de.tum.informatics.www1.artemis.native_app.core.ui.generateLinks +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.MetisContext import de.tum.informatics.www1.artemis.native_app.feature.metis.ui.canDisplayMetisOnDisplaySide +import io.github.fornewid.placeholder.material3.placeholder import io.ktor.http.HttpHeaders import io.ktor.http.URLBuilder import io.ktor.http.appendPathSegments @@ -71,7 +69,7 @@ fun NavGraphBuilder.lecture( onParticipateInQuiz: (courseId: Long, exerciseId: Long, isPractice: Boolean) -> Unit, onClickViewQuizResults: (courseId: Long, exerciseId: Long) -> Unit, ) { - composable( + animatedComposable( deepLinks = listOf( navDeepLink { uriPattern = "artemis://lectures/{lectureId}" diff --git a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/AccountUi.kt b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/AccountUi.kt index b092a427e..278bdca04 100644 --- a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/AccountUi.kt +++ b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/AccountUi.kt @@ -3,9 +3,6 @@ package de.tum.informatics.www1.artemis.native_app.feature.login import androidx.compose.animation.AnimatedContent import androidx.compose.animation.Crossfade import androidx.compose.animation.core.tween -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally -import androidx.compose.animation.togetherWith import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -26,13 +23,15 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Settings import androidx.compose.material3.Button -import androidx.compose.material3.Divider +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -55,7 +54,6 @@ import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.toRoute @@ -66,6 +64,8 @@ import de.tum.informatics.www1.artemis.native_app.core.model.server_config.Profi import de.tum.informatics.www1.artemis.native_app.core.ui.Spacings import de.tum.informatics.www1.artemis.native_app.core.ui.common.BasicDataStateUi import de.tum.informatics.www1.artemis.native_app.core.ui.material.colors.linkTextColor +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.DefaultTransition +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable import de.tum.informatics.www1.artemis.native_app.feature.login.custom_instance_selection.CustomInstanceSelectionScreen import de.tum.informatics.www1.artemis.native_app.feature.login.instance_selection.InstanceSelectionScreen import de.tum.informatics.www1.artemis.native_app.feature.login.login.LoginScreen @@ -74,6 +74,7 @@ import de.tum.informatics.www1.artemis.native_app.feature.login.register.Registe import de.tum.informatics.www1.artemis.native_app.feature.login.saml2_login.Saml2LoginScreen import de.tum.informatics.www1.artemis.native_app.feature.login.saml2_login.Saml2LoginViewModel import de.tum.informatics.www1.artemis.native_app.feature.login.service.ServerNotificationStorageService +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.serialization.Serializable @@ -83,7 +84,6 @@ import org.koin.core.parameter.parametersOf import java.io.IOException private const val ARG_REMEMBER_ME = "rememberMe" -private const val NESTED_SAML2_LOGIN_ROUTE = "saml2_login" @Serializable private sealed interface NestedDestination { @@ -127,7 +127,9 @@ fun NavGraphBuilder.loginScreen( onFinishedLoginFlow: (deepLink: String?) -> Unit, onRequestOpenSettings: () -> Unit ) { - composable { + animatedComposable( + enterTransition = { DefaultTransition.fadeIn }, + ) { val screen = it.toRoute() val nextDestinationValue = screen.nextDestination @@ -148,9 +150,7 @@ fun NavGraphBuilder.loginScreen( AnimatedContent( targetState = currentContent, transitionSpec = { - // Animation is always the same - slideInHorizontally { width -> width } togetherWith - slideOutHorizontally { width -> -width } + DefaultTransition.navigateForward }, label = "Login <-> Notification configuration" ) { content -> @@ -260,6 +260,10 @@ private fun LoginUiScreen( ) } ) { paddingValues -> + val sheetState = rememberModalBottomSheetState() + val scope = rememberCoroutineScope() + var showBottomSheet by remember { mutableStateOf(false) } + NavHost( modifier = Modifier .fillMaxSize() @@ -269,7 +273,7 @@ private fun LoginUiScreen( navController = nestedNavController, startDestination = if (hasSelectedInstance) NestedDestination.Home else NestedDestination.InstanceSelection ) { - composable() { + animatedComposable { AccountScreen( modifier = Modifier.fillMaxSize(), canSwitchInstance = !BuildConfig.hasInstanceRestriction, @@ -281,19 +285,14 @@ private fun LoginUiScreen( }, onNavigateToInstanceSelection = { onNavigatedToInstanceSelection() - - nestedNavController.navigate(NestedDestination.InstanceSelection) { - popUpTo { - inclusive = true - } - } + showBottomSheet = true }, onLoggedIn = onLoggedIn, onClickSaml2Login = onClickSaml2Login ) } - composable { + animatedComposable { CustomInstanceSelectionScreen( modifier = Modifier .fillMaxSize() @@ -307,7 +306,7 @@ private fun LoginUiScreen( } } - composable { + animatedComposable { LoginScreen( modifier = Modifier.fillMaxSize(), viewModel = koinViewModel(), @@ -316,7 +315,7 @@ private fun LoginUiScreen( ) } - composable { backStack -> + animatedComposable { backStack -> val rememberMe = backStack.arguments?.getBoolean(ARG_REMEMBER_ME) checkNotNull(rememberMe) @@ -330,7 +329,7 @@ private fun LoginUiScreen( ) } - composable { + animatedComposable { RegisterUi( modifier = Modifier .fillMaxSize() @@ -343,29 +342,44 @@ private fun LoginUiScreen( } ) } + } - composable { - val scope = rememberCoroutineScope() + val hideBottomSheet: (action: (suspend CoroutineScope.() -> Unit)?) -> Unit = { action -> + scope.launch { + if (action != null) { + action() + } + sheetState.hide() + }.invokeOnCompletion { + if (!sheetState.isVisible) { + showBottomSheet = false + } + } + } + if (showBottomSheet) { + ModalBottomSheet( + onDismissRequest = { + showBottomSheet = false + }, + sheetState = sheetState + ) { InstanceSelectionScreen( modifier = Modifier .fillMaxSize() .padding(horizontal = Spacings.ScreenHorizontalSpacing), availableInstances = ArtemisInstances.instances, onSelectArtemisInstance = { serverUrl -> - scope.launch { + hideBottomSheet { serverConfigurationService.updateServerUrl(serverUrl) - nestedNavController.navigate(NestedDestination.Home) { - popUpTo { - inclusive = true - } - } } }, onRequestOpenCustomInstanceSelection = { - nestedNavController.navigate( - NestedDestination.CustomInstanceSelection - ) + hideBottomSheet { + nestedNavController.navigate( + NestedDestination.CustomInstanceSelection + ) + } } ) } @@ -451,6 +465,7 @@ private fun AccountUi( bottom = WindowInsets.systemBars .asPaddingValues() .calculateBottomPadding() + + 8.dp ), text = AnnotatedString(stringResource(id = R.string.account_change_artemis_instance_label)), style = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.linkTextColor), @@ -574,7 +589,7 @@ private fun LoginOrRegister( onClickLogin ) - Divider( + HorizontalDivider( modifier = Modifier .fillMaxWidth() .padding(start = 8.dp, end = 8.dp, top = 24.dp, bottom = 8.dp) diff --git a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/instance_selection/InstanceSelectionScreen.kt b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/instance_selection/InstanceSelectionScreen.kt index 21560bdbf..f7dc4e843 100644 --- a/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/instance_selection/InstanceSelectionScreen.kt +++ b/feature/login/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/login/instance_selection/InstanceSelectionScreen.kt @@ -1,31 +1,26 @@ package de.tum.informatics.www1.artemis.native_app.feature.login.instance_selection import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.systemBars -import androidx.compose.foundation.lazy.grid.GridCells -import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -35,8 +30,6 @@ import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import coil3.request.ImageRequest import de.tum.informatics.www1.artemis.native_app.core.datastore.defaults.ArtemisInstances -import de.tum.informatics.www1.artemis.native_app.core.ui.getWindowSizeClass -import de.tum.informatics.www1.artemis.native_app.feature.login.ArtemisHeader import de.tum.informatics.www1.artemis.native_app.feature.login.R @Composable @@ -50,20 +43,6 @@ internal fun InstanceSelectionScreen( modifier = modifier, verticalArrangement = Arrangement.spacedBy(8.dp) ) { - Box( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(0.05f) - ) - - ArtemisHeader(modifier = Modifier.fillMaxWidth()) - - Box( - modifier = Modifier - .fillMaxWidth() - .fillMaxHeight(0.05f) - ) - Text( modifier = Modifier.fillMaxWidth(), text = stringResource(id = R.string.account_select_artemis_instance_select_text), @@ -71,22 +50,17 @@ internal fun InstanceSelectionScreen( fontWeight = FontWeight.Bold ) - val windowSizeClass = getWindowSizeClass() - val columnCount = when { - windowSizeClass.widthSizeClass > WindowWidthSizeClass.Expanded -> 6 - windowSizeClass.widthSizeClass > WindowWidthSizeClass.Medium -> 4 - else -> 2 - } - - LazyVerticalGrid( - columns = GridCells.Fixed(columnCount), - horizontalArrangement = Arrangement.spacedBy(16.dp), + LazyColumn( verticalArrangement = Arrangement.spacedBy(16.dp), contentPadding = PaddingValues( bottom = WindowInsets.systemBars.asPaddingValues().calculateBottomPadding() ) ) { - items(availableInstances) { instance -> + items( + count = availableInstances.size, + key = { availableInstances[it].serverUrl } + ) { index -> + val instance = availableInstances[index] val item = GridCellItem.ArtemisInstanceGridCellItem( instance = instance, imageUrl = "${instance.serverUrl}public/images/logo.png" @@ -118,14 +92,15 @@ private fun ArtemisInstanceGridCell( val context = LocalContext.current Card(modifier = modifier, onClick = onClick) { - Column( + Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically ) { val imageModifier = Modifier - .fillMaxWidth() + .height(64.dp) .aspectRatio(1f) when (item) { @@ -153,25 +128,11 @@ private fun ArtemisInstanceGridCell( } } - val name = item.getName() - - var threeLineName: String by remember(name) { - mutableStateOf(name) - } - Text( modifier = Modifier.fillMaxWidth(), - textAlign = TextAlign.Center, - text = threeLineName, + textAlign = TextAlign.Left, + text = item.getName(), style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - maxLines = 3, - onTextLayout = { - if (it.lineCount < 3) { - val additionalLines = 3 - it.lineCount - threeLineName += "\n".repeat(additionalLines) - } - } ) } } diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationScreen.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationScreen.kt index 43de8d786..19dd85cb7 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationScreen.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationScreen.kt @@ -1,25 +1,29 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui +import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.background +import androidx.compose.animation.SizeTransform import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn -import androidx.compose.material3.DividerDefaults +import androidx.compose.material3.VerticalDivider import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner import de.tum.informatics.www1.artemis.native_app.core.ui.getWindowSizeClass +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.DefaultTransition import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.StandalonePostId import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf @@ -70,26 +74,50 @@ fun ConversationScreen( } val widthSizeClass = getWindowSizeClass().widthSizeClass + var showThread by remember { mutableStateOf(threadPostId != null) } + + val onNavigateUp = { + onCloseThread() + showThread = false + } + val onClickViewPost = { clientPostId: StandalonePostId -> + onOpenThread(clientPostId) + showThread = true + } when { widthSizeClass <= WindowWidthSizeClass.Compact -> { Box(modifier = modifier) { - ConversationChatListScreen( - modifier = Modifier.fillMaxSize(), - viewModel = viewModel, - courseId = courseId, - conversationId = conversationId, - onNavigateBack = onCloseConversation, - onNavigateToSettings = onNavigateToSettings, - onClickViewPost = { clientPostId -> onOpenThread(clientPostId) } - ) - - if (threadPostId != null) { - ConversationThreadScreen( - modifier = Modifier.fillMaxSize(), - viewModel = viewModel, - onNavigateUp = onCloseThread - ) + AnimatedContent( + targetState = showThread, + transitionSpec = { + if (targetState) { + DefaultTransition.navigateForward + } else { + DefaultTransition.navigateBack + }.using( + SizeTransform(clip = false) + ) + }, + label = "ConversationScreen chatList thread navigation animation" + ) { _showThread -> + if (_showThread) { + ConversationThreadScreen( + modifier = Modifier.fillMaxSize(), + viewModel = viewModel, + onNavigateUp = onNavigateUp + ) + } else { + ConversationChatListScreen( + modifier = Modifier.fillMaxSize(), + viewModel = viewModel, + courseId = courseId, + conversationId = conversationId, + onNavigateBack = onCloseConversation, + onNavigateToSettings = onNavigateToSettings, + onClickViewPost = onClickViewPost + ) + } } } } @@ -101,8 +129,7 @@ fun ConversationScreen( modifier = modifier, horizontalArrangement = arrangement ) { - val isOverviewVisible = - threadPostId == null || widthSizeClass >= WindowWidthSizeClass.Expanded + val isOverviewVisible = !showThread || widthSizeClass >= WindowWidthSizeClass.Expanded AnimatedVisibility( modifier = Modifier .weight(ConversationOverviewMaxWeight) @@ -125,8 +152,8 @@ fun ConversationScreen( } val otherWeight = when { - isOverviewVisible && threadPostId != null -> 0.35f - isOverviewVisible && threadPostId == null -> 0.7f + isOverviewVisible && showThread -> 0.35f + isOverviewVisible && !showThread -> 0.7f else -> 0.5f } @@ -143,29 +170,19 @@ fun ConversationScreen( conversationId = conversationId, onNavigateBack = onCloseConversation, onNavigateToSettings = onNavigateToSettings, - onClickViewPost = { clientPostId -> onOpenThread(clientPostId) } + onClickViewPost = onClickViewPost ) - if (threadPostId != null) { + if (showThread) { VerticalDivider() ConversationThreadScreen( modifier = otherModifier, viewModel = viewModel, - onNavigateUp = onCloseThread + onNavigateUp = onNavigateUp ) } } } } } - -@Composable -private fun VerticalDivider() { - Box( - modifier = Modifier - .fillMaxHeight() - .width(1.dp) - .background(DividerDefaults.color) - ) -} diff --git a/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ConversationConfiguration.kt b/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ConversationConfiguration.kt new file mode 100644 index 000000000..1e402d2cd --- /dev/null +++ b/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ConversationConfiguration.kt @@ -0,0 +1,59 @@ +package de.tum.informatics.www1.artemis.native_app.feature.metis + +import android.os.Parcelable +import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.StandalonePostId +import kotlinx.parcelize.Parcelize + +@Parcelize +sealed class ConversationConfiguration( + /** + * Used to determine the animation when switching between configurations. + */ + val navigationLevel: Int +) : Parcelable + +@Parcelize +data object NothingOpened : ConversationConfiguration(0) + +@Parcelize +data class OpenedConversation(val conversationId: Long, val openedThread: OpenedThread?) : + ConversationConfiguration(1) + +@Parcelize +data class OpenedThread(val conversationId: Long, val postId: StandalonePostId) : Parcelable + +/** + * Special configuration in which we want to navigate to the 1-to-1 conversation with the user with the specified username. + * In this configuration, we simply show a loading bar while we load the necessary data to show the chat. + */ +@Parcelize +data class NavigateToUserConversation(val username: String) : ConversationConfiguration(0) + +@Parcelize +internal data class AddChannelConfiguration( + val prevConfiguration: ConversationConfiguration +) : + ConversationConfiguration(1) + +@Parcelize +internal data class BrowseChannelConfiguration( + val prevConfiguration: ConversationConfiguration +) : + ConversationConfiguration(1) + +@Parcelize +internal data class CreatePersonalConversation(val prevConfiguration: ConversationConfiguration) : + ConversationConfiguration(1) + +@Parcelize +internal data class ConversationSettings( + val conversationId: Long, + val prevConfiguration: ConversationConfiguration, + val isAddingMembers: Boolean = false, + val isViewingAllMembers: Boolean = false +) : ConversationConfiguration( + navigationLevel = when(prevConfiguration) { + is ConversationSettings -> 3 + else -> 2 + } +) \ No newline at end of file diff --git a/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ui/ConversationFacadeUi.kt b/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ui/ConversationFacadeUi.kt index 7b256bbb9..d7c621bba 100644 --- a/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ui/ConversationFacadeUi.kt +++ b/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ui/ConversationFacadeUi.kt @@ -3,6 +3,8 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.ui import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import de.tum.informatics.www1.artemis.native_app.feature.metis.ConversationConfiguration +import de.tum.informatics.www1.artemis.native_app.feature.metis.NothingOpened import de.tum.informatics.www1.artemis.native_app.feature.metis.codeofconduct.ui.CodeOfConductFacadeUi import org.koin.androidx.compose.koinViewModel import org.koin.core.parameter.parametersOf diff --git a/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ui/SinglePageConversationBody.kt b/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ui/SinglePageConversationBody.kt index a629e7c79..3ea79f654 100644 --- a/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ui/SinglePageConversationBody.kt +++ b/feature/metis/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/ui/SinglePageConversationBody.kt @@ -1,7 +1,8 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.ui -import android.os.Parcelable import androidx.activity.compose.BackHandler +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.SizeTransform import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -11,6 +12,16 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.DefaultTransition +import de.tum.informatics.www1.artemis.native_app.feature.metis.AddChannelConfiguration +import de.tum.informatics.www1.artemis.native_app.feature.metis.BrowseChannelConfiguration +import de.tum.informatics.www1.artemis.native_app.feature.metis.ConversationConfiguration +import de.tum.informatics.www1.artemis.native_app.feature.metis.ConversationSettings +import de.tum.informatics.www1.artemis.native_app.feature.metis.CreatePersonalConversation +import de.tum.informatics.www1.artemis.native_app.feature.metis.NavigateToUserConversation +import de.tum.informatics.www1.artemis.native_app.feature.metis.NothingOpened +import de.tum.informatics.www1.artemis.native_app.feature.metis.OpenedConversation +import de.tum.informatics.www1.artemis.native_app.feature.metis.OpenedThread import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui.ConversationScreen import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ui.conversation.browse_channels.BrowseChannelsScreen import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ui.conversation.create_channel.CreateChannelScreen @@ -19,8 +30,6 @@ import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversati import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ui.conversation.settings.add_members.ConversationAddMembersScreen import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ui.conversation.settings.members.ConversationMembersScreen import de.tum.informatics.www1.artemis.native_app.feature.metis.manageconversations.ui.conversation.settings.overview.ConversationSettingsScreen -import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.content.StandalonePostId -import kotlinx.parcelize.Parcelize @Composable internal fun SinglePageConversationBody( @@ -76,170 +85,143 @@ internal fun SinglePageConversationBody( ) } - when (val config = configuration) { - NothingOpened -> { - conversationOverview(modifier) - } - - is OpenedConversation -> { - ConversationScreen( - modifier = modifier, - conversationId = config.conversationId, - threadPostId = config.openedThread?.postId, - courseId = courseId, - onOpenThread = { postId -> - configuration = OpenedConversation( - config.conversationId, - OpenedThread(config.conversationId, postId) - ) - }, - onCloseThread = { - configuration = config.copy(openedThread = null) - }, - onCloseConversation = { - configuration = NothingOpened - }, - onNavigateToSettings = { - configuration = ConversationSettings( - conversationId = config.conversationId, - prevConfiguration = config - ) - }, - conversationsOverview = { mod -> conversationOverview(mod) } + AnimatedContent( + targetState = configuration, + transitionSpec = { + val navigationLevelDiff = targetState.navigationLevel - initialState.navigationLevel + when (navigationLevelDiff) { + 1 -> DefaultTransition.navigateForward + -1 -> DefaultTransition.navigateBack + else -> DefaultTransition.navigateNeutral + } + .using( + SizeTransform(clip = false) ) - } + }, + contentKey = { + it.javaClass // Eg no recomposition of the ChatList when navigating to a thread. + }, + label = "SinglePageConversationBody screen transition animation" + ) { config -> + when (config) { + NothingOpened -> { + conversationOverview(modifier) + } - is BrowseChannelConfiguration -> { - BrowseChannelsScreen( - modifier = modifier, - courseId = courseId, - onNavigateToConversation = openConversation, - //onNavigateToCreateChannel = {}, - onNavigateBack = { configuration = config.prevConfiguration } - ) - } + is OpenedConversation -> { + ConversationScreen( + modifier = modifier, + conversationId = config.conversationId, + threadPostId = config.openedThread?.postId, + courseId = courseId, + onOpenThread = { postId -> + configuration = OpenedConversation( + config.conversationId, + OpenedThread(config.conversationId, postId) + ) + }, + onCloseThread = { + configuration = config.copy(openedThread = null) + }, + onCloseConversation = { + configuration = NothingOpened + }, + onNavigateToSettings = { + configuration = ConversationSettings( + conversationId = config.conversationId, + prevConfiguration = config + ) + }, + conversationsOverview = { mod -> conversationOverview(mod) } + ) + } - is AddChannelConfiguration -> { - if (canCreateChannel) { - CreateChannelScreen( + is BrowseChannelConfiguration -> { + BrowseChannelsScreen( modifier = modifier, courseId = courseId, - onConversationCreated = openConversation, + onNavigateToConversation = openConversation, onNavigateBack = { configuration = config.prevConfiguration } ) } - } - - is CreatePersonalConversation -> { - CreatePersonalConversationScreen( - modifier = modifier, - courseId = courseId, - onConversationCreated = openConversation, - onNavigateBack = { configuration = config.prevConfiguration } - ) - } - is ConversationSettings -> { - when { - config.isViewingAllMembers -> { - ConversationMembersScreen( + is AddChannelConfiguration -> { + if (canCreateChannel) { + CreateChannelScreen( modifier = modifier, courseId = courseId, - conversationId = config.conversationId, + onConversationCreated = openConversation, onNavigateBack = { configuration = config.prevConfiguration } ) } + } - config.isAddingMembers -> { - ConversationAddMembersScreen( - modifier = modifier, - courseId = courseId, - conversationId = config.conversationId, - onNavigateBack = { configuration = config.prevConfiguration } - ) - } + is CreatePersonalConversation -> { + CreatePersonalConversationScreen( + modifier = modifier, + courseId = courseId, + onConversationCreated = openConversation, + onNavigateBack = { configuration = config.prevConfiguration } + ) + } - else -> { - ConversationSettingsScreen( - modifier = modifier, - courseId = courseId, - conversationId = config.conversationId, - onNavigateBack = { configuration = config.prevConfiguration }, - onRequestAddMembers = { - configuration = config.copy( - isAddingMembers = true, - prevConfiguration = configuration - ) - }, - onRequestViewAllMembers = { - configuration = config.copy( - isViewingAllMembers = true, - prevConfiguration = configuration - ) - }, - onConversationLeft = { - configuration = NothingOpened - } - ) + is ConversationSettings -> { + when { + config.isViewingAllMembers -> { + ConversationMembersScreen( + modifier = modifier, + courseId = courseId, + conversationId = config.conversationId, + onNavigateBack = { configuration = config.prevConfiguration } + ) + } + + config.isAddingMembers -> { + ConversationAddMembersScreen( + modifier = modifier, + courseId = courseId, + conversationId = config.conversationId, + onNavigateBack = { configuration = config.prevConfiguration } + ) + } + + else -> { + ConversationSettingsScreen( + modifier = modifier, + courseId = courseId, + conversationId = config.conversationId, + onNavigateBack = { configuration = config.prevConfiguration }, + onRequestAddMembers = { + configuration = config.copy( + isAddingMembers = true, + prevConfiguration = configuration + ) + }, + onRequestViewAllMembers = { + configuration = config.copy( + isViewingAllMembers = true, + prevConfiguration = configuration + ) + }, + onConversationLeft = { + configuration = NothingOpened + } + ) + } } } - } - is NavigateToUserConversation -> { - NavigateToUserConversationUi( - modifier = modifier, - courseId = courseId, - username = config.username, - onNavigateToConversation = { conversationId -> - configuration = OpenedConversation(conversationId, null) - }, - onNavigateBack = { configuration = NothingOpened } - ) + is NavigateToUserConversation -> { + NavigateToUserConversationUi( + modifier = modifier, + courseId = courseId, + username = config.username, + onNavigateToConversation = { conversationId -> + configuration = OpenedConversation(conversationId, null) + }, + onNavigateBack = { configuration = NothingOpened } + ) + } } } } - - -@Parcelize -sealed interface ConversationConfiguration : Parcelable - -@Parcelize -data object NothingOpened : ConversationConfiguration - -@Parcelize -data class OpenedConversation(val conversationId: Long, val openedThread: OpenedThread?) : - ConversationConfiguration - -@Parcelize -data class OpenedThread(val conversationId: Long, val postId: StandalonePostId) : Parcelable - -/** - * Special configuration in which we want to navigate to the 1-to-1 conversation with the user with the specified username. - * In this configuration, we simply show a loading bar while we load the necessary data to show the chat. - */ -@Parcelize -data class NavigateToUserConversation(val username: String) : ConversationConfiguration - -@Parcelize -private data class AddChannelConfiguration( - val prevConfiguration: ConversationConfiguration -) : - ConversationConfiguration - -@Parcelize -private data class BrowseChannelConfiguration( - val prevConfiguration: ConversationConfiguration -) : - ConversationConfiguration - -@Parcelize -private data class CreatePersonalConversation(val prevConfiguration: ConversationConfiguration) : - ConversationConfiguration - -@Parcelize -private data class ConversationSettings( - val conversationId: Long, - val prevConfiguration: ConversationConfiguration, - val isAddingMembers: Boolean = false, - val isViewingAllMembers: Boolean = false -) : ConversationConfiguration diff --git a/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/participation/QuizParticipationScreen.kt b/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/participation/QuizParticipationScreen.kt index 79dfe92d1..cfae93ece 100644 --- a/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/participation/QuizParticipationScreen.kt +++ b/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/participation/QuizParticipationScreen.kt @@ -26,7 +26,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable import androidx.navigation.navDeepLink import androidx.navigation.toRoute import de.tum.informatics.www1.artemis.native_app.core.data.service.impl.JsonProvider @@ -35,6 +34,7 @@ import de.tum.informatics.www1.artemis.native_app.core.ui.alert.DestructiveMarkd import de.tum.informatics.www1.artemis.native_app.core.ui.alert.TextAlertDialog import de.tum.informatics.www1.artemis.native_app.core.ui.common.ButtonWithLoadingAnimation import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.KSerializableNavType +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable import de.tum.informatics.www1.artemis.native_app.feature.quiz.QuizType import de.tum.informatics.www1.artemis.native_app.feature.quiz.R import de.tum.informatics.www1.artemis.native_app.feature.quiz.view_result.ViewQuizResultScreen @@ -68,7 +68,7 @@ fun NavController.navigateToQuizParticipation( } fun NavGraphBuilder.quizParticipation(onLeaveQuiz: () -> Unit) { - composable( + animatedComposable( typeMap = mapOf( typeOf() to KSerializableNavType( isNullableAllowed = false, diff --git a/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/view_result/ViewQuizResultScreen.kt b/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/view_result/ViewQuizResultScreen.kt index 6362488a7..ddb173bcd 100644 --- a/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/view_result/ViewQuizResultScreen.kt +++ b/feature/quiz/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/quiz/view_result/ViewQuizResultScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable import androidx.navigation.toRoute import de.tum.informatics.www1.artemis.native_app.core.data.DataState import de.tum.informatics.www1.artemis.native_app.core.data.join @@ -25,6 +24,7 @@ import de.tum.informatics.www1.artemis.native_app.core.model.exercise.QuizExerci import de.tum.informatics.www1.artemis.native_app.core.model.exercise.submission.QuizSubmission import de.tum.informatics.www1.artemis.native_app.core.model.exercise.submission.Result import de.tum.informatics.www1.artemis.native_app.core.ui.common.BasicDataStateUi +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable import de.tum.informatics.www1.artemis.native_app.feature.quiz.QuizType import de.tum.informatics.www1.artemis.native_app.feature.quiz.R import de.tum.informatics.www1.artemis.native_app.feature.quiz.participation.QuizQuestionData @@ -43,7 +43,7 @@ fun NavController.navigateToQuizResult( } fun NavGraphBuilder.quizResults(onRequestLeaveQuizResults: () -> Unit) { - composable { backStackEntry -> + animatedComposable { backStackEntry -> val route: ViewQuizResultScreen = backStackEntry.toRoute() ViewQuizResultScreen( diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts index 4e76737dd..6e7520d0d 100644 --- a/feature/settings/build.gradle.kts +++ b/feature/settings/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(project(":core:device")) implementation(project(":core:datastore")) implementation(project(":feature:push")) + implementation(project(":feature:login")) implementation(libs.placeholder.material) implementation(libs.androidx.browser) diff --git a/feature/settings/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/settings/SettingsScreen.kt b/feature/settings/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/settings/SettingsScreen.kt index e7887c7aa..fb97d1cc6 100644 --- a/feature/settings/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/settings/SettingsScreen.kt +++ b/feature/settings/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/settings/SettingsScreen.kt @@ -44,7 +44,6 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.NavOptionsBuilder -import androidx.navigation.compose.composable import de.tum.informatics.www1.artemis.native_app.core.common.flatMapLatest import de.tum.informatics.www1.artemis.native_app.core.data.DataState import de.tum.informatics.www1.artemis.native_app.core.data.retryOnInternet @@ -55,6 +54,9 @@ import de.tum.informatics.www1.artemis.native_app.core.device.NetworkStatusProvi import de.tum.informatics.www1.artemis.native_app.core.model.account.Account import de.tum.informatics.www1.artemis.native_app.core.ui.LocalLinkOpener import de.tum.informatics.www1.artemis.native_app.core.ui.common.EmptyDataStateUi +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.DefaultTransition +import de.tum.informatics.www1.artemis.native_app.core.ui.navigation.animatedComposable +import de.tum.informatics.www1.artemis.native_app.feature.login.LoginScreen import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.ui.profile_picture.ProfilePicture import de.tum.informatics.www1.artemis.native_app.feature.metis.shared.ui.profile_picture.ProfilePictureData import de.tum.informatics.www1.artemis.native_app.feature.push.service.PushNotificationConfigurationService @@ -91,7 +93,15 @@ fun NavGraphBuilder.settingsScreen( onLoggedOut: () -> Unit, onDisplayThirdPartyLicenses: () -> Unit ) { - composable { + animatedComposable( + exitTransition = { + val toLoginScreen = targetState.destination.route?.startsWith(LoginScreen::class.qualifiedName!!) ?: false + if (toLoginScreen) { + return@animatedComposable DefaultTransition.fadeOut + } + DefaultTransition.exit + } + ) { SettingsScreen( modifier = Modifier.fillMaxSize(), versionCode = versionCode, @@ -104,7 +114,7 @@ fun NavGraphBuilder.settingsScreen( } } - composable { + animatedComposable { PushNotificationSettingsScreen( modifier = Modifier.fillMaxSize(), onNavigateBack = onNavigateUp From 276f8599a3a0ed452a08bd61e863b10cd945a58a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Dec 2024 19:26:42 +0100 Subject: [PATCH 15/16] Bump koin from 4.0.0 to 4.0.1 (#255) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Martin Felber <45291671+FelberMartin@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8353c3225..292ae5b3a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ kotlin = "2.1.0" kotlinxCoroutines = "1.10.1" kotlinxDatetime = "0.6.1" kotlinxSerializationJson = "1.7.3" -koin = "4.0.0" +koin = "4.0.1" koinAndroidxCompose = "4.0.0" kover = "0.9.0" krossbow = "8.1.0" From f599e43b8847520aeae5cf37a6b35e9c511b558c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:47:34 +0100 Subject: [PATCH 16/16] Bump krossbow from 8.1.0 to 8.2.0 (#261) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 292ae5b3a..d8c3d7cc6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -25,7 +25,7 @@ kotlinxSerializationJson = "1.7.3" koin = "4.0.1" koinAndroidxCompose = "4.0.0" kover = "0.9.0" -krossbow = "8.1.0" +krossbow = "8.2.0" ktor = "3.0.3" markwon = "4.6.2" mockk = "1.13.14"