From 985d173ee2cb5028742f6858543574f97f14d0bc Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Fri, 25 Oct 2024 17:49:55 +0200 Subject: [PATCH 1/6] Fixed AutoCompletionPopup clipping --- .../metis/conversation/ui/reply/ReplyAutoCompletePopup.kt | 4 +++- .../feature/metis/conversation/ui/reply/ReplyTextField.kt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt index 7e34a609f..d608f2cf2 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.window.PopupProperties import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.R private val HintHorizontalPadding = 16.dp +private val PopupTopPadding = 8.dp @Composable internal fun ReplyAutoCompletePopup( @@ -46,7 +47,8 @@ internal fun ReplyAutoCompletePopup( ) { ReplyAutoCompletePopupBody( modifier = Modifier - .heightIn(max = maxHeight) + .padding(top = PopupTopPadding) + .heightIn(max = maxHeight - PopupTopPadding) .width(targetWidth), autoCompleteCategories = autoCompleteCategories, performAutoComplete = performAutoComplete diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextField.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextField.kt index 83cc9c20a..cb633b5d7 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextField.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextField.kt @@ -202,7 +202,7 @@ private fun CreateReplyUi( .onSizeChanged { textFieldWidth = it.width } .padding(vertical = 8.dp, horizontal = 8.dp) .onGloballyPositioned { coordinates -> - val textFieldWindowTopLeft = coordinates.localToWindow(Offset.Zero) + val textFieldWindowTopLeft = coordinates.localToRoot(Offset.Zero) popupMaxHeight = textFieldWindowTopLeft.y.toInt() } .testTag(TEST_TAG_REPLY_TEXT_FIELD), From 221bc893d43424ff434e8bbe1c4d9caf5e406fe1 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Fri, 25 Oct 2024 17:59:32 +0200 Subject: [PATCH 2/6] Fixed confusing behaviour: searching for channels, lectures and exercises is no longer case sensitive --- .../feature/metis/conversation/ui/ConversationViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt index a9df8b73f..af56ab066 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/ConversationViewModel.kt @@ -448,7 +448,7 @@ internal open class ConversationViewModel( val exerciseAutoCompleteItems = course .exercises - .filter { query in it.title.orEmpty() } + .filter { it.title.orEmpty().contains(query, ignoreCase = true) } .mapNotNull { exercise -> val exerciseTag = when (exercise) { is FileUploadExercise -> "file-upload" @@ -498,7 +498,7 @@ internal open class ConversationViewModel( conversationsDataState.bind { conversations -> val conversationAutoCompleteItems = conversations .filterIsInstance() - .filter { query in it.name } + .filter { it.name.contains(query, ignoreCase = true) } .map { channel -> AutoCompleteHint( hint = channel.name, From 66d64131ff906cc96aa658519aea8e79d5ed8c7e Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Sat, 9 Nov 2024 15:52:10 +0100 Subject: [PATCH 3/6] Added maxHeight and adjusted design --- .../ui/reply/ReplyAutoCompletePopup.kt | 22 +++++---- .../conversation/ui/reply/ReplyTextField.kt | 48 ++++++++++++++----- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt index d608f2cf2..b5f756fda 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt @@ -11,17 +11,18 @@ import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Divider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.min import androidx.compose.ui.util.fastForEachIndexed import androidx.compose.ui.window.Popup import androidx.compose.ui.window.PopupPositionProvider @@ -29,13 +30,14 @@ import androidx.compose.ui.window.PopupProperties import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.R private val HintHorizontalPadding = 16.dp -private val PopupTopPadding = 8.dp +private val PopupVerticalPadding = 8.dp +private val AutoCompletionDialogMaxHeight = 270.dp @Composable internal fun ReplyAutoCompletePopup( popupPositionProvider: PopupPositionProvider, targetWidth: Dp, - maxHeight: Dp, + maxHeightFromScreen: Dp, autoCompleteCategories: List, performAutoComplete: (replacement: String) -> Unit, onDismissRequest: () -> Unit @@ -45,10 +47,11 @@ internal fun ReplyAutoCompletePopup( properties = PopupProperties(dismissOnClickOutside = true), onDismissRequest = onDismissRequest ) { + val maxHeight = min(maxHeightFromScreen, AutoCompletionDialogMaxHeight) ReplyAutoCompletePopupBody( modifier = Modifier - .padding(top = PopupTopPadding) - .heightIn(max = maxHeight - PopupTopPadding) + .padding(vertical = PopupVerticalPadding) + .heightIn(max = maxHeight - PopupVerticalPadding * 2) .width(targetWidth), autoCompleteCategories = autoCompleteCategories, performAutoComplete = performAutoComplete @@ -64,12 +67,11 @@ private fun ReplyAutoCompletePopupBody( ) { LazyColumn( modifier = modifier - .background( - color = MaterialTheme.colorScheme.surfaceVariant, - shape = RoundedCornerShape(topStart = 45f, topEnd = 45f) - ) - .padding(top = 8.dp) + .clip(MaterialTheme.shapes.large) + .background(color = MaterialTheme.colorScheme.surfaceVariant) + .padding(8.dp) ) { + autoCompleteCategories.forEachIndexed { categoryIndex, category -> item { AutoCompleteCategoryComposable( diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextField.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextField.kt index cb633b5d7..5c330e561 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextField.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextField.kt @@ -2,15 +2,37 @@ package de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.ui import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.Done import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Send -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +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.focus.FocusRequester @@ -57,7 +79,7 @@ internal fun ReplyTextField( Surface( modifier = modifier.defaultMinSize(minHeight = 48.dp), color = MaterialTheme.colorScheme.surfaceVariant, - shape = RoundedCornerShape(5) + shape = MaterialTheme.shapes.large ) { Box( modifier = Modifier @@ -172,14 +194,16 @@ private fun CreateReplyUi( val autoCompleteHints = manageAutoCompleteHints(currentTextFieldValue) var textFieldWidth by remember { mutableIntStateOf(0) } - var popupMaxHeight by remember { mutableStateOf(0) } + var popupMaxHeight by remember { mutableIntStateOf(0) } - if (autoCompleteHints.orEmpty().flatMap { it.items } - .isNotEmpty() && mayShowAutoCompletePopup) { + val showAutoCompletePopup = mayShowAutoCompletePopup + && autoCompleteHints.orEmpty().flatMap { it.items }.isNotEmpty() + + if (showAutoCompletePopup) { ReplyAutoCompletePopup( autoCompleteCategories = autoCompleteHints.orEmpty(), targetWidth = with(LocalDensity.current) { textFieldWidth.toDp() }, - maxHeight = with(LocalDensity.current) { popupMaxHeight.toDp() }, + maxHeightFromScreen = with(LocalDensity.current) { popupMaxHeight.toDp() }, popupPositionProvider = ReplyAutoCompletePopupPositionProvider, performAutoComplete = { replacement -> replyMode.onUpdate( @@ -200,11 +224,11 @@ private fun CreateReplyUi( modifier = Modifier .fillMaxWidth() .onSizeChanged { textFieldWidth = it.width } - .padding(vertical = 8.dp, horizontal = 8.dp) .onGloballyPositioned { coordinates -> - val textFieldWindowTopLeft = coordinates.localToRoot(Offset.Zero) - popupMaxHeight = textFieldWindowTopLeft.y.toInt() + val textFieldRootTopLeft = coordinates.localToRoot(Offset.Zero) + popupMaxHeight = textFieldRootTopLeft.y.toInt() } + .padding(8.dp) .testTag(TEST_TAG_REPLY_TEXT_FIELD), textFieldValue = currentTextFieldValue, onTextChanged = replyMode::onUpdate, From ebf910e2621e91803530b51f898cc879940893c5 Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Thu, 14 Nov 2024 09:37:05 +0100 Subject: [PATCH 4/6] Reducing minSdk 31 -> 30 as this is not required by any dependencies --- 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 c832b0626..1b8007277 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] compileSdk = "35" targetSdk = "34" -minSdk = "31" +minSdk = "30" buildToolsVersion = "35.0.0" accompanist = "0.30.1" From 6205e0986baa6d5a776b1a5df104d03d410ee47c Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Thu, 14 Nov 2024 09:42:06 +0100 Subject: [PATCH 5/6] Updating dark outlineVariant to be visible against dark surfaceVariant --- .../tum/informatics/www1/artemis/native_app/android/ui/Color.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/Color.kt b/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/Color.kt index 1ffee10f2..ecb818267 100644 --- a/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/Color.kt +++ b/app/src/main/java/de/tum/informatics/www1/artemis/native_app/android/ui/Color.kt @@ -61,7 +61,7 @@ val md_theme_dark_inverseSurface = Color(0xFFE2E2E5) val md_theme_dark_inversePrimary = Color(0xFF006398) val md_theme_dark_shadow = Color(0xFF000000) val md_theme_dark_surfaceTint = Color(0xFF93CCFF) -val md_theme_dark_outlineVariant = Color(0xFF42474E) +val md_theme_dark_outlineVariant = Color(0xFF55515B) val md_theme_dark_scrim = Color(0xFF000000) From 9e5a5a9f39fc177b88bd9cdcb803ff2cca92490e Mon Sep 17 00:00:00 2001 From: Martin Felber Date: Thu, 14 Nov 2024 14:59:54 +0100 Subject: [PATCH 6/6] Fix tests --- .../metis/conversation/ui/reply/ReplyAutoCompletePopup.kt | 4 ++++ .../metis/conversation/ui/reply/ReplyTextFieldUiTest.kt | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt index b5f756fda..d21cb1a08 100644 --- a/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt +++ b/feature/metis/conversation/src/main/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyAutoCompletePopup.kt @@ -17,6 +17,7 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -29,6 +30,8 @@ import androidx.compose.ui.window.PopupPositionProvider import androidx.compose.ui.window.PopupProperties import de.tum.informatics.www1.artemis.native_app.feature.metis.conversation.R +const val TEST_TAG_REPLY_AUTO_COMPLETE_POPUP_LIST = "TEST_TAG_REPLY_AUTO_COMPLETE_POPUP_LIST" + private val HintHorizontalPadding = 16.dp private val PopupVerticalPadding = 8.dp private val AutoCompletionDialogMaxHeight = 270.dp @@ -70,6 +73,7 @@ private fun ReplyAutoCompletePopupBody( .clip(MaterialTheme.shapes.large) .background(color = MaterialTheme.colorScheme.surfaceVariant) .padding(8.dp) + .testTag(TEST_TAG_REPLY_AUTO_COMPLETE_POPUP_LIST) ) { autoCompleteCategories.forEachIndexed { categoryIndex, category -> diff --git a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextFieldUiTest.kt b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextFieldUiTest.kt index 93c4a6edc..f723ea766 100644 --- a/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextFieldUiTest.kt +++ b/feature/metis/conversation/src/test/kotlin/de/tum/informatics/www1/artemis/native_app/feature/metis/conversation/ui/reply/ReplyTextFieldUiTest.kt @@ -10,9 +10,10 @@ import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.performTextInput import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performScrollToIndex import androidx.compose.ui.test.performTextClearance +import androidx.compose.ui.test.performTextInput import androidx.compose.ui.text.input.TextFieldValue import androidx.test.ext.junit.runners.AndroidJUnit4 import de.tum.informatics.www1.artemis.native_app.core.data.DataState @@ -140,7 +141,9 @@ class ReplyTextFieldUiTest { private fun ComposeContentTestRule.assertAllAutoCompletionHintsShown() { onNodeWithText("User1").assertExists() + onNodeWithTag(TEST_TAG_REPLY_AUTO_COMPLETE_POPUP_LIST).performScrollToIndex(1) onNodeWithText("User2").assertExists() + onNodeWithTag(TEST_TAG_REPLY_AUTO_COMPLETE_POPUP_LIST).performScrollToIndex(2) onNodeWithText("User3").assertExists() } } \ No newline at end of file