From 95ea10b305b0ceeb476f491cd5725b7968e269d0 Mon Sep 17 00:00:00 2001 From: Minecon724 Date: Sun, 22 Jun 2025 17:12:07 +0200 Subject: [PATCH] Move stuff around --- .../chatapp/activity/chat/ChatActivity.kt | 164 +++--------------- .../activity/chat/ChatActivityUiState.kt | 4 +- .../activity/chat/ChatActivityViewModel.kt | 39 +++-- .../activity/chat/composable/ChatToolBar.kt | 76 ++++++++ .../composable/NestedScrollKeyboardHider.kt | 16 -- .../composable/thread/ChatMessageComposer.kt | 34 ++++ .../thread/ChatResponseErrorNotice.kt | 47 +++++ .../ChatComposerState.kt} | 14 +- .../chat/{ => state}/ChatResponseError.kt | 2 +- .../composable/AnimatedChangingText.kt | 6 +- .../DisableBringIntoViewOnFocus.kt} | 6 +- .../composable/LessBasicTextField.kt} | 7 +- .../composable/NestedScrollKeyboardHider.kt | 25 +++ 13 files changed, 250 insertions(+), 190 deletions(-) create mode 100644 app/src/main/java/eu/m724/chatapp/activity/chat/composable/ChatToolBar.kt delete mode 100644 app/src/main/java/eu/m724/chatapp/activity/chat/composable/NestedScrollKeyboardHider.kt create mode 100644 app/src/main/java/eu/m724/chatapp/activity/chat/composable/thread/ChatMessageComposer.kt create mode 100644 app/src/main/java/eu/m724/chatapp/activity/chat/composable/thread/ChatResponseErrorNotice.kt rename app/src/main/java/eu/m724/chatapp/activity/chat/{ChatState.kt => state/ChatComposerState.kt} (74%) rename app/src/main/java/eu/m724/chatapp/activity/chat/{ => state}/ChatResponseError.kt (80%) rename app/src/main/java/eu/m724/chatapp/activity/{chat => ui}/composable/AnimatedChangingText.kt (87%) rename app/src/main/java/eu/m724/chatapp/activity/{chat/composable/DisableBringIntoViewOnFocusNode.kt => ui/composable/DisableBringIntoViewOnFocus.kt} (89%) rename app/src/main/java/eu/m724/chatapp/activity/{chat/composable/SimpleTextFieldWithPadding.kt => ui/composable/LessBasicTextField.kt} (91%) create mode 100644 app/src/main/java/eu/m724/chatapp/activity/ui/composable/NestedScrollKeyboardHider.kt diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivity.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivity.kt index 56aa3ad..7571654 100644 --- a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivity.kt +++ b/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivity.kt @@ -5,20 +5,15 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels -import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.foundation.clickable -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.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width @@ -26,17 +21,8 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.Send -import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.CenterAlignedTopAppBar -import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.IconButtonDefaults -import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost @@ -57,21 +43,21 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint import eu.m724.chatapp.R -import eu.m724.chatapp.activity.chat.ChatState.Companion.rememberChatState -import eu.m724.chatapp.activity.chat.composable.AnimatedChangingText +import eu.m724.chatapp.activity.chat.composable.ChatToolBar +import eu.m724.chatapp.activity.chat.state.ChatComposerState.Companion.rememberChatComposerState +import eu.m724.chatapp.activity.ui.composable.AnimatedChangingText import eu.m724.chatapp.activity.chat.composable.LanguageModelMistakeWarning -import eu.m724.chatapp.activity.chat.composable.NestedScrollKeyboardHider -import eu.m724.chatapp.activity.chat.composable.SimpleTextFieldWithPadding -import eu.m724.chatapp.activity.chat.composable.disableBringIntoViewOnFocus +import eu.m724.chatapp.activity.chat.composable.thread.ChatMessageComposer +import eu.m724.chatapp.activity.chat.composable.thread.ChatResponseErrorNotice +import eu.m724.chatapp.activity.chat.state.ChatComposerState +import eu.m724.chatapp.activity.ui.composable.disableBringIntoViewOnFocus +import eu.m724.chatapp.activity.ui.composable.hideKeyboardOnScrollUp import eu.m724.chatapp.activity.ui.theme.ChatAppTheme import eu.m724.chatapp.api.data.response.completion.ChatMessage import kotlinx.coroutines.launch @@ -91,10 +77,9 @@ class ChatActivity : ComponentActivity() { val uiState by viewModel.uiState.collectAsStateWithLifecycle() val softwareKeyboardController = LocalSoftwareKeyboardController.current - val context = LocalContext.current val coroutineScope = rememberCoroutineScope() - val chatState = rememberChatState() + val chatState = rememberChatComposerState() val threadViewLazyListState = rememberLazyListState() val snackbarHostState = remember { SnackbarHostState() } @@ -111,7 +96,7 @@ class ChatActivity : ComponentActivity() { ChatScreen( windowSizeClass = windowSizeClass, uiState = uiState, - chatState = chatState, + chatComposerState = chatState, threadViewLazyListState = threadViewLazyListState, snackbarHostState = snackbarHostState, onSend = onSend, @@ -181,7 +166,7 @@ class ChatActivity : ComponentActivity() { fun ChatScreen( windowSizeClass: WindowSizeClass, uiState: ChatActivityUiState, - chatState: ChatState, + chatComposerState: ChatComposerState, threadViewLazyListState: LazyListState, snackbarHostState: SnackbarHostState, onSend: () -> Unit, @@ -212,7 +197,7 @@ fun ChatScreen( .padding(innerPadding), isTablet = isTablet, uiState = uiState, - chatState = chatState, + chatComposerState = chatComposerState, threadViewLazyListState = threadViewLazyListState, onSend = onSend, onRequestFocus = onRequestFocus @@ -226,7 +211,7 @@ fun ChatScreenContent( modifier: Modifier = Modifier, isTablet: Boolean, uiState: ChatActivityUiState, - chatState: ChatState, + chatComposerState: ChatComposerState, threadViewLazyListState: LazyListState, onSend: () -> Unit, onRequestFocus: () -> Unit @@ -263,7 +248,7 @@ fun ChatScreenContent( lazyListState = threadViewLazyListState, messages = uiState.messages, uiState = uiState, - chatState = chatState + chatComposerState = chatComposerState ) }, { @@ -275,8 +260,8 @@ fun ChatScreenContent( horizontalAlignment = Alignment.CenterHorizontally ) { ChatToolBar( - canSend = (chatState.composerValue.isNotBlank() || uiState.lastResponseError != null) && !uiState.requestInProgress, - canRestart = chatState.composerValue.isBlank() && uiState.lastResponseError != null, + canSend = (chatComposerState.composerValue.isNotBlank() || uiState.lastResponseError != null) && !uiState.requestInProgress, + canRestart = chatComposerState.composerValue.isBlank() && uiState.lastResponseError != null, onSend = onSend, onEmptySpaceClick = onRequestFocus ) @@ -295,16 +280,14 @@ fun ThreadView( lazyListState: LazyListState, messages: List, uiState: ChatActivityUiState, - chatState: ChatState, + chatComposerState: ChatComposerState, modifier: Modifier = Modifier ) { val localSoftwareKeyboardController = LocalSoftwareKeyboardController.current LazyColumn( modifier = modifier - .nestedScroll( // Hides the keyboard when scrolling - NestedScrollKeyboardHider(localSoftwareKeyboardController) - ), + .hideKeyboardOnScrollUp(localSoftwareKeyboardController!!), state = lazyListState ) { items(messages) { message -> @@ -323,7 +306,7 @@ fun ThreadView( if (uiState.lastResponseError != null) { item(key = "error") { - ResponseErrorDisplay( + ChatResponseErrorNotice( error = uiState.lastResponseError ) } @@ -335,10 +318,10 @@ fun ThreadView( modifier = Modifier .fillParentMaxHeight() // so that you can click anywhere on the screen to focus the text field .disableBringIntoViewOnFocus() - .focusRequester(chatState.focusRequester), - value = chatState.composerValue, + .focusRequester(chatComposerState.focusRequester), + value = chatComposerState.composerValue, onValueChange = { - chatState.composerValue = it + chatComposerState.composerValue = it } ) } else { @@ -381,36 +364,6 @@ fun ChatMessagePrompt( ) } -@Composable -fun ResponseErrorDisplay( - error: ChatResponseError, - modifier: Modifier = Modifier -) { - Row( - modifier = modifier, - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - modifier = Modifier - .height(18.dp) - .padding(horizontal = 4.dp), - imageVector = Icons.Default.Warning, - contentDescription = stringResource(R.string.response_error_icon_description), - tint = MaterialTheme.colorScheme.error - ) - - val errorMessage = when (error) { - is ChatResponseError.LengthLimit -> stringResource(R.string.response_error_length_limit) - is ChatResponseError.Error -> stringResource(R.string.response_error_generic) - } - - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error - ) - } -} - @Composable fun ChatMessageResponse( content: String, @@ -422,80 +375,7 @@ fun ChatMessageResponse( ) } -@Composable -fun ChatMessageComposer( - value: String, - onValueChange: (String) -> Unit, - modifier: Modifier = Modifier -) { - SimpleTextFieldWithPadding( - modifier = modifier, - value = value, - onValueChange = onValueChange, - placeholder = { - Text( - text = stringResource(R.string.composer_placeholder_type) - ) // TODO hide when just browsing history? - }, - padding = PaddingValues(vertical = 10.dp), - textStyle = LocalTextStyle.current.copy( - color = MaterialTheme.colorScheme.onSurface - ) - ) -} -@Composable -fun ChatToolBar( - modifier: Modifier = Modifier, - canSend: Boolean, - canRestart: Boolean, - onSend: () -> Unit, - onEmptySpaceClick: () -> Unit -) { - val sendButtonColor by animateColorAsState( - targetValue = if (canSend) { - IconButtonDefaults.iconButtonColors().contentColor - } else { - IconButtonDefaults.iconButtonColors().disabledContentColor - }, label = "sendButtonColor" - ) - - ElevatedCard( - modifier = modifier, - shape = RoundedCornerShape(24.dp) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = onEmptySpaceClick), - horizontalArrangement = Arrangement.End - ) { - IconButton( - onClick = onSend, - modifier = Modifier - .height(48.dp) - .padding(horizontal = 8.dp), - enabled = canSend, - colors = IconButtonDefaults.iconButtonColors( - contentColor = sendButtonColor, - disabledContentColor = sendButtonColor - ) - ) { - if (canRestart) { - Icon( - painter = painterResource(R.drawable.outline_restart_alt_24), - contentDescription = stringResource(R.string.button_send_restart_icon_description) - ) - } else { - Icon( - imageVector = Icons.AutoMirrored.Filled.Send, - contentDescription = stringResource(R.string.button_send_icon_description) - ) - } - } - } - } -} @OptIn(ExperimentalMaterial3Api::class) @Composable diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivityUiState.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivityUiState.kt index 28366a7..f45642d 100644 --- a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivityUiState.kt +++ b/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivityUiState.kt @@ -1,5 +1,6 @@ package eu.m724.chatapp.activity.chat +import eu.m724.chatapp.activity.chat.state.ChatResponseError import eu.m724.chatapp.api.data.response.completion.ChatMessage data class ChatActivityUiState( @@ -19,5 +20,4 @@ data class ChatActivityUiState( * All messages in the chat */ val messages: List = emptyList() -) - +) \ No newline at end of file diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivityViewModel.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivityViewModel.kt index 3a841e4..920bd0f 100644 --- a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivityViewModel.kt +++ b/app/src/main/java/eu/m724/chatapp/activity/chat/ChatActivityViewModel.kt @@ -3,6 +3,7 @@ package eu.m724.chatapp.activity.chat import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import eu.m724.chatapp.activity.chat.state.ChatResponseError import eu.m724.chatapp.api.AiApiService import eu.m724.chatapp.api.data.request.completion.ChatCompletionRequest import eu.m724.chatapp.api.data.response.completion.ChatCompletionResponseEvent @@ -58,10 +59,12 @@ class ChatActivityViewModel @Inject constructor( messages.removeLast() } // If there was an error and no response was generated, this shouldn't be a follow-up - messages.add(ChatMessage( - role = ChatMessage.Role.User, - content = promptContent - )) + messages.add( + ChatMessage( + role = ChatMessage.Role.User, + content = promptContent + ) + ) _uiState.update { it.copy( @@ -75,14 +78,16 @@ class ChatActivityViewModel @Inject constructor( ) } - aiApiService.getChatCompletion(ChatCompletionRequest( - model = "fre-model", - messages = messages, - temperature = 1.0f, - maxTokens = 4, - frequencyPenalty = 0.0f, - presencePenalty = 0.0f - )).onEach { event -> + aiApiService.getChatCompletion( + ChatCompletionRequest( + model = "free-model", + messages = messages, + temperature = 1.0f, + maxTokens = 4, + frequencyPenalty = 0.0f, + presencePenalty = 0.0f + ) + ).onEach { event -> when (event) { is SseEvent.Open -> { // There is nothing to do here @@ -110,10 +115,12 @@ class ChatActivityViewModel @Inject constructor( is SseEvent.Closed -> { // Closed is not used in case of an error - messages.add(ChatMessage( - role = ChatMessage.Role.Assistant, - content = responseContent - )) + messages.add( + ChatMessage( + role = ChatMessage.Role.Assistant, + content = responseContent + ) + ) } is SseEvent.Failure -> { // The below should do. More investigation is needed but I believe this should do diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/ChatToolBar.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/composable/ChatToolBar.kt new file mode 100644 index 0000000..f7630e8 --- /dev/null +++ b/app/src/main/java/eu/m724/chatapp/activity/chat/composable/ChatToolBar.kt @@ -0,0 +1,76 @@ +package eu.m724.chatapp.activity.chat.composable + +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.Send +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import eu.m724.chatapp.R + +@Composable +fun ChatToolBar( + modifier: Modifier = Modifier, + canSend: Boolean, + canRestart: Boolean, + onSend: () -> Unit, + onEmptySpaceClick: () -> Unit +) { + val sendButtonColor by animateColorAsState( + targetValue = if (canSend) { + IconButtonDefaults.iconButtonColors().contentColor + } else { + IconButtonDefaults.iconButtonColors().disabledContentColor + }, label = "sendButtonColor" + ) + + ElevatedCard( + modifier = modifier, + shape = RoundedCornerShape(24.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onEmptySpaceClick), + horizontalArrangement = Arrangement.End + ) { + IconButton( + onClick = onSend, + modifier = Modifier + .height(48.dp) + .padding(horizontal = 8.dp), + enabled = canSend, + colors = IconButtonDefaults.iconButtonColors( + contentColor = sendButtonColor, + disabledContentColor = sendButtonColor + ) + ) { + if (canRestart) { + Icon( + painter = painterResource(R.drawable.outline_restart_alt_24), + contentDescription = stringResource(R.string.button_send_restart_icon_description) + ) + } else { + Icon( + imageVector = Icons.AutoMirrored.Filled.Send, + contentDescription = stringResource(R.string.button_send_icon_description) + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/NestedScrollKeyboardHider.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/composable/NestedScrollKeyboardHider.kt deleted file mode 100644 index bd597f3..0000000 --- a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/NestedScrollKeyboardHider.kt +++ /dev/null @@ -1,16 +0,0 @@ -package eu.m724.chatapp.activity.chat.composable - -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.platform.SoftwareKeyboardController - -class NestedScrollKeyboardHider( - private val softwareKeyboardController: SoftwareKeyboardController? -) : NestedScrollConnection { - override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { - if (available.y > 0) // if scrolling up (content up, finger down) - softwareKeyboardController?.hide() - return Offset.Companion.Zero - } -} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/thread/ChatMessageComposer.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/composable/thread/ChatMessageComposer.kt new file mode 100644 index 0000000..1e89d8f --- /dev/null +++ b/app/src/main/java/eu/m724/chatapp/activity/chat/composable/thread/ChatMessageComposer.kt @@ -0,0 +1,34 @@ +package eu.m724.chatapp.activity.chat.composable.thread + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import eu.m724.chatapp.R +import eu.m724.chatapp.activity.ui.composable.LessBasicTextField + +@Composable +fun ChatMessageComposer( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier = Modifier +) { + LessBasicTextField( + modifier = modifier, + value = value, + onValueChange = onValueChange, + placeholder = { + Text( + text = stringResource(R.string.composer_placeholder_type) + ) // TODO hide when just browsing history? + }, + padding = PaddingValues(vertical = 10.dp), + textStyle = LocalTextStyle.current.copy( + color = MaterialTheme.colorScheme.onSurface + ) + ) +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/thread/ChatResponseErrorNotice.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/composable/thread/ChatResponseErrorNotice.kt new file mode 100644 index 0000000..7e05ccd --- /dev/null +++ b/app/src/main/java/eu/m724/chatapp/activity/chat/composable/thread/ChatResponseErrorNotice.kt @@ -0,0 +1,47 @@ +package eu.m724.chatapp.activity.chat.composable.thread + +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import eu.m724.chatapp.R +import eu.m724.chatapp.activity.chat.state.ChatResponseError + +@Composable +fun ChatResponseErrorNotice( + error: ChatResponseError, + modifier: Modifier = Modifier +) { + Row( + modifier = modifier, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + modifier = Modifier + .height(18.dp) + .padding(horizontal = 4.dp), + imageVector = Icons.Default.Warning, + contentDescription = stringResource(R.string.response_error_icon_description), + tint = MaterialTheme.colorScheme.error + ) + + val errorMessage = when (error) { + is ChatResponseError.LengthLimit -> stringResource(R.string.response_error_length_limit) + is ChatResponseError.Error -> stringResource(R.string.response_error_generic) + } + + Text( + text = errorMessage, + color = MaterialTheme.colorScheme.error + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatState.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/state/ChatComposerState.kt similarity index 74% rename from app/src/main/java/eu/m724/chatapp/activity/chat/ChatState.kt rename to app/src/main/java/eu/m724/chatapp/activity/chat/state/ChatComposerState.kt index bf775d7..570ddfa 100644 --- a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatState.kt +++ b/app/src/main/java/eu/m724/chatapp/activity/chat/state/ChatComposerState.kt @@ -1,4 +1,4 @@ -package eu.m724.chatapp.activity.chat +package eu.m724.chatapp.activity.chat.state import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -7,7 +7,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.focus.FocusRequester -class ChatState( +class ChatComposerState( val focusRequester: FocusRequester ) { var composerValue by mutableStateOf("") @@ -18,14 +18,12 @@ class ChatState( companion object { @Composable - fun rememberChatState( + fun rememberChatComposerState( focusRequester: FocusRequester = remember { FocusRequester() } - ): ChatState { + ): ChatComposerState { return remember { - ChatState(focusRequester = focusRequester) + ChatComposerState(focusRequester = focusRequester) } } } -} - - +} \ No newline at end of file diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatResponseError.kt b/app/src/main/java/eu/m724/chatapp/activity/chat/state/ChatResponseError.kt similarity index 80% rename from app/src/main/java/eu/m724/chatapp/activity/chat/ChatResponseError.kt rename to app/src/main/java/eu/m724/chatapp/activity/chat/state/ChatResponseError.kt index 87735e2..a951dd8 100644 --- a/app/src/main/java/eu/m724/chatapp/activity/chat/ChatResponseError.kt +++ b/app/src/main/java/eu/m724/chatapp/activity/chat/state/ChatResponseError.kt @@ -1,4 +1,4 @@ -package eu.m724.chatapp.activity.chat +package eu.m724.chatapp.activity.chat.state sealed interface ChatResponseError { // TODO does this belong here? data object LengthLimit: ChatResponseError diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/AnimatedChangingText.kt b/app/src/main/java/eu/m724/chatapp/activity/ui/composable/AnimatedChangingText.kt similarity index 87% rename from app/src/main/java/eu/m724/chatapp/activity/chat/composable/AnimatedChangingText.kt rename to app/src/main/java/eu/m724/chatapp/activity/ui/composable/AnimatedChangingText.kt index 5542417..9f967d4 100644 --- a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/AnimatedChangingText.kt +++ b/app/src/main/java/eu/m724/chatapp/activity/ui/composable/AnimatedChangingText.kt @@ -1,4 +1,4 @@ -package eu.m724.chatapp.activity.chat.composable +package eu.m724.chatapp.activity.ui.composable import androidx.compose.animation.AnimatedContent import androidx.compose.animation.core.tween @@ -11,6 +11,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow +/** + * A text that transitions nicely when changed. + * Not appropriate for completing text. + */ @Composable fun AnimatedChangingText( text: String, diff --git a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/DisableBringIntoViewOnFocusNode.kt b/app/src/main/java/eu/m724/chatapp/activity/ui/composable/DisableBringIntoViewOnFocus.kt similarity index 89% rename from app/src/main/java/eu/m724/chatapp/activity/chat/composable/DisableBringIntoViewOnFocusNode.kt rename to app/src/main/java/eu/m724/chatapp/activity/ui/composable/DisableBringIntoViewOnFocus.kt index 1f25aa7..70a7a84 100644 --- a/app/src/main/java/eu/m724/chatapp/activity/chat/composable/DisableBringIntoViewOnFocusNode.kt +++ b/app/src/main/java/eu/m724/chatapp/activity/ui/composable/DisableBringIntoViewOnFocus.kt @@ -1,4 +1,4 @@ -package eu.m724.chatapp.activity.chat.composable +package eu.m724.chatapp.activity.ui.composable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Rect @@ -40,5 +40,7 @@ private class DisableBringIntoViewOnFocusElement : ModifierNodeElement Unit, placeholder: @Composable () -> Unit, diff --git a/app/src/main/java/eu/m724/chatapp/activity/ui/composable/NestedScrollKeyboardHider.kt b/app/src/main/java/eu/m724/chatapp/activity/ui/composable/NestedScrollKeyboardHider.kt new file mode 100644 index 0000000..6937534 --- /dev/null +++ b/app/src/main/java/eu/m724/chatapp/activity/ui/composable/NestedScrollKeyboardHider.kt @@ -0,0 +1,25 @@ +package eu.m724.chatapp.activity.ui.composable + +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.SoftwareKeyboardController + +class NestedScrollKeyboardHider( + private val softwareKeyboardController: SoftwareKeyboardController +) : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + if (available.y > 0) // scroll up (content up, finger down) + softwareKeyboardController.hide() + return Offset.Companion.Zero + } +} + +/** + * Hides the keyboard when scrolling up in a column. + */ +fun Modifier.hideKeyboardOnScrollUp( + softwareKeyboardController: SoftwareKeyboardController +): Modifier = this.nestedScroll(NestedScrollKeyboardHider(softwareKeyboardController)) \ No newline at end of file