From 4870678ad552b4f6ea2364957204b8a8189b0c9d Mon Sep 17 00:00:00 2001 From: the-only-queen-anna Date: Wed, 1 Jul 2026 19:42:39 +0200 Subject: [PATCH 1/4] fix: add restriction to user input, no emojis allowed --- .../frontend/ui/inputs/AppTextField.kt | 29 +++++++++++++++++-- .../frontend/ui/screens/GameboardScreen.kt | 3 ++ .../frontend/ui/screens/JoinLobbyScreen.kt | 2 ++ .../frontend/ui/screens/UserNameScreen.kt | 8 +++-- 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/codenames/frontend/ui/inputs/AppTextField.kt b/app/src/main/java/com/codenames/frontend/ui/inputs/AppTextField.kt index 0de4235..b21b7b0 100644 --- a/app/src/main/java/com/codenames/frontend/ui/inputs/AppTextField.kt +++ b/app/src/main/java/com/codenames/frontend/ui/inputs/AppTextField.kt @@ -53,6 +53,26 @@ data class AppTextFieldKeyboard( val actions: KeyboardActions = KeyboardActions.Default, ) +typealias InputFilter = (String) -> String + +object InputFilters { + + val NONE: InputFilter = { it } + + val ALPHANUMERIC: InputFilter = { + it.filter { c -> c.isLetterOrDigit() || c.isWhitespace() } + } + + val LOBBY_CODE: InputFilter = { + it.filter { c -> c.isLetterOrDigit() } + .uppercase() + } + + val DIGIT: InputFilter = { + it.filter { c -> c.isDigit() } + } +} + @Suppress("ktlint:standard:function-naming") @Composable fun AppTextField( @@ -62,6 +82,7 @@ fun AppTextField( state: AppTextFieldState = AppTextFieldState(), style: AppTextFieldStyle = AppTextFieldStyle(), keyboard: AppTextFieldKeyboard = AppTextFieldKeyboard(), + inputFilter: InputFilter = InputFilters.NONE ) { val resolvedContainerColor = resolveContainerColor(style.containerColor) val resolvedContentColor = resolveContentColor(style.contentColor) @@ -76,7 +97,9 @@ fun AppTextField( AppTextFieldType.PRIMARY -> { PrimaryAppTextField( value = value, - onValueChange = onValueChange, + onValueChange = { newValue -> + val filtered = inputFilter.invoke(newValue) + onValueChange(filtered) }, modifier = modifier, state = state, style = style, @@ -90,7 +113,9 @@ fun AppTextField( AppTextFieldType.SECONDARY -> { SecondaryAppTextField( value = value, - onValueChange = onValueChange, + onValueChange = { newValue -> + val filtered = inputFilter.invoke(newValue) + onValueChange(filtered) }, modifier = modifier, state = state, style = style, diff --git a/app/src/main/java/com/codenames/frontend/ui/screens/GameboardScreen.kt b/app/src/main/java/com/codenames/frontend/ui/screens/GameboardScreen.kt index 9ca0e63..2239249 100644 --- a/app/src/main/java/com/codenames/frontend/ui/screens/GameboardScreen.kt +++ b/app/src/main/java/com/codenames/frontend/ui/screens/GameboardScreen.kt @@ -66,6 +66,7 @@ import com.codenames.frontend.ui.inputs.AppTextField import com.codenames.frontend.ui.inputs.AppTextFieldKeyboard import com.codenames.frontend.ui.inputs.AppTextFieldState import com.codenames.frontend.ui.inputs.AppTextFieldStyle +import com.codenames.frontend.ui.inputs.InputFilters import com.codenames.frontend.ui.roles.PlayerRoles import com.codenames.frontend.ui.theme.AppBackground import com.codenames.frontend.ui.theme.AppBlack @@ -988,6 +989,7 @@ fun HintSection( }, ), ), + inputFilter = InputFilters.ALPHANUMERIC ) AppTextField( @@ -1010,6 +1012,7 @@ fun HintSection( keyboardType = KeyboardType.Number, ), ), + inputFilter = InputFilters.DIGIT ) AppSendButton( diff --git a/app/src/main/java/com/codenames/frontend/ui/screens/JoinLobbyScreen.kt b/app/src/main/java/com/codenames/frontend/ui/screens/JoinLobbyScreen.kt index 863b2c6..88b02ba 100644 --- a/app/src/main/java/com/codenames/frontend/ui/screens/JoinLobbyScreen.kt +++ b/app/src/main/java/com/codenames/frontend/ui/screens/JoinLobbyScreen.kt @@ -36,6 +36,7 @@ import com.codenames.frontend.ui.inputs.AppTextFieldKeyboard import com.codenames.frontend.ui.inputs.AppTextFieldState import com.codenames.frontend.ui.inputs.AppTextFieldStyle import com.codenames.frontend.ui.inputs.AppTextFieldType +import com.codenames.frontend.ui.inputs.InputFilters import com.codenames.frontend.ui.navigation.Screen import com.codenames.frontend.ui.theme.AppBackground import com.codenames.frontend.ui.theme.AppInk @@ -144,6 +145,7 @@ fun JoinlobbyScreen( onDone = { submitJoin() }, ), ), + inputFilter = InputFilters.LOBBY_CODE ) AppButton( diff --git a/app/src/main/java/com/codenames/frontend/ui/screens/UserNameScreen.kt b/app/src/main/java/com/codenames/frontend/ui/screens/UserNameScreen.kt index 1241da5..14dddb9 100644 --- a/app/src/main/java/com/codenames/frontend/ui/screens/UserNameScreen.kt +++ b/app/src/main/java/com/codenames/frontend/ui/screens/UserNameScreen.kt @@ -30,6 +30,9 @@ import com.codenames.frontend.data.model.enums.ConnectionState import com.codenames.frontend.ui.buttons.AppButton import com.codenames.frontend.ui.buttons.AppButtonStyle import com.codenames.frontend.ui.buttons.SettingsCornerButton +import com.codenames.frontend.ui.inputs.AppTextField +import com.codenames.frontend.ui.inputs.AppTextFieldState +import com.codenames.frontend.ui.inputs.InputFilters import com.codenames.frontend.ui.navigation.Screen import com.codenames.frontend.ui.roles.PlayerRoles import com.codenames.frontend.ui.theme.AppBackground @@ -105,11 +108,12 @@ fun UserNameScreen( modifier = Modifier.padding(bottom = dimensions.sectionSpacing * 3), ) - TextField( + AppTextField( value = username, onValueChange = { username = it }, - label = { Text("enter username") }, + state = AppTextFieldState(label = "Enter username", placeholder = "user"), modifier = Modifier.fillMaxWidth(if (dimensions.isNarrowWidth) 0.7f else 0.5f), + inputFilter = InputFilters.ALPHANUMERIC, ) Spacer(modifier = Modifier.height(dimensions.itemSpacing)) From c7793265ebe689fb5a16150014f23b21dcbd9b18 Mon Sep 17 00:00:00 2001 From: the-only-queen-anna Date: Wed, 1 Jul 2026 19:44:39 +0200 Subject: [PATCH 2/4] fix: remove flickering when returning to home screen + add reset of chat state after a game --- .../frontend/ui/screens/GameScreenWrapper.kt | 8 +++++++- .../com/codenames/frontend/viewmodel/ChatViewModel.kt | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/codenames/frontend/ui/screens/GameScreenWrapper.kt b/app/src/main/java/com/codenames/frontend/ui/screens/GameScreenWrapper.kt index 46a47eb..3c775a3 100644 --- a/app/src/main/java/com/codenames/frontend/ui/screens/GameScreenWrapper.kt +++ b/app/src/main/java/com/codenames/frontend/ui/screens/GameScreenWrapper.kt @@ -88,8 +88,14 @@ fun GameScreenWrapper( navController.navigate(Screen.Settings.route) }, onReturnToHome = { - navController.navigate(Screen.Start.route) + navController.navigate(Screen.Start.route) { + popUpTo(navController.graph.startDestinationId) { + inclusive = true + } + launchSingleTop = true + } lobbyViewModel.cleanup() + chatViewModel.cleanup() gameViewModel.resetGameState() }, ) diff --git a/app/src/main/java/com/codenames/frontend/viewmodel/ChatViewModel.kt b/app/src/main/java/com/codenames/frontend/viewmodel/ChatViewModel.kt index 24fc029..912c2f7 100644 --- a/app/src/main/java/com/codenames/frontend/viewmodel/ChatViewModel.kt +++ b/app/src/main/java/com/codenames/frontend/viewmodel/ChatViewModel.kt @@ -212,4 +212,15 @@ class ChatViewModel private fun setError(error: Throwable) { _errorMessage.value = ErrorMessageMapper.toUserMessage(error) } + + fun cleanup() { + viewModelScope.launch { + _errorMessage.update { + null + } + _chatState.update { + ChatLists() + } + } + } } From d8a0a0018c6873183e3407d8d117d24da63ab99b Mon Sep 17 00:00:00 2001 From: the-only-queen-anna Date: Wed, 1 Jul 2026 20:04:07 +0200 Subject: [PATCH 3/4] test: add test --- .../frontend/viewmodel/ChatViewModelTest.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/test/java/com/codenames/frontend/viewmodel/ChatViewModelTest.kt b/app/src/test/java/com/codenames/frontend/viewmodel/ChatViewModelTest.kt index 8a31688..49bf459 100644 --- a/app/src/test/java/com/codenames/frontend/viewmodel/ChatViewModelTest.kt +++ b/app/src/test/java/com/codenames/frontend/viewmodel/ChatViewModelTest.kt @@ -1,6 +1,7 @@ package com.codenames.frontend.viewmodel import com.codenames.frontend.data.model.ChatDomainModel +import com.codenames.frontend.data.model.ChatLists import com.codenames.frontend.data.model.enums.ChatTab import com.codenames.frontend.data.model.enums.Role import com.codenames.frontend.data.model.enums.Team @@ -24,6 +25,7 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test +import kotlin.collections.emptyList @OptIn(ExperimentalCoroutinesApi::class) class ChatViewModelTest { @@ -338,4 +340,14 @@ class ChatViewModelTest { assertNull(viewModel.errorMessage.value) } + @Test + fun testClearChatState() = + runTest { + + viewModel.cleanup() + + assertEquals(emptyList(), viewModel.chatState.value.lobbyMessages) + assertEquals(emptyList(), viewModel.chatState.value.teamMessages) + assertEquals(emptyList(), viewModel.chatState.value.operativeMessages) + } } From 1a6c45ad6f6f4e8945961bd52524fc1256b2f9ac Mon Sep 17 00:00:00 2001 From: the-only-queen-anna Date: Wed, 1 Jul 2026 20:05:10 +0200 Subject: [PATCH 4/4] format: reformat code --- .../com/codenames/frontend/ui/inputs/AppTextField.kt | 12 +++++++----- .../codenames/frontend/ui/screens/GameboardScreen.kt | 4 ++-- .../codenames/frontend/ui/screens/JoinLobbyScreen.kt | 2 +- .../codenames/frontend/ui/screens/UserNameScreen.kt | 1 - .../frontend/viewmodel/ChatViewModelTest.kt | 5 ++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/codenames/frontend/ui/inputs/AppTextField.kt b/app/src/main/java/com/codenames/frontend/ui/inputs/AppTextField.kt index b21b7b0..a79a542 100644 --- a/app/src/main/java/com/codenames/frontend/ui/inputs/AppTextField.kt +++ b/app/src/main/java/com/codenames/frontend/ui/inputs/AppTextField.kt @@ -56,7 +56,6 @@ data class AppTextFieldKeyboard( typealias InputFilter = (String) -> String object InputFilters { - val NONE: InputFilter = { it } val ALPHANUMERIC: InputFilter = { @@ -64,7 +63,8 @@ object InputFilters { } val LOBBY_CODE: InputFilter = { - it.filter { c -> c.isLetterOrDigit() } + it + .filter { c -> c.isLetterOrDigit() } .uppercase() } @@ -82,7 +82,7 @@ fun AppTextField( state: AppTextFieldState = AppTextFieldState(), style: AppTextFieldStyle = AppTextFieldStyle(), keyboard: AppTextFieldKeyboard = AppTextFieldKeyboard(), - inputFilter: InputFilter = InputFilters.NONE + inputFilter: InputFilter = InputFilters.NONE, ) { val resolvedContainerColor = resolveContainerColor(style.containerColor) val resolvedContentColor = resolveContentColor(style.contentColor) @@ -99,7 +99,8 @@ fun AppTextField( value = value, onValueChange = { newValue -> val filtered = inputFilter.invoke(newValue) - onValueChange(filtered) }, + onValueChange(filtered) + }, modifier = modifier, state = state, style = style, @@ -115,7 +116,8 @@ fun AppTextField( value = value, onValueChange = { newValue -> val filtered = inputFilter.invoke(newValue) - onValueChange(filtered) }, + onValueChange(filtered) + }, modifier = modifier, state = state, style = style, diff --git a/app/src/main/java/com/codenames/frontend/ui/screens/GameboardScreen.kt b/app/src/main/java/com/codenames/frontend/ui/screens/GameboardScreen.kt index 2239249..2482cc2 100644 --- a/app/src/main/java/com/codenames/frontend/ui/screens/GameboardScreen.kt +++ b/app/src/main/java/com/codenames/frontend/ui/screens/GameboardScreen.kt @@ -989,7 +989,7 @@ fun HintSection( }, ), ), - inputFilter = InputFilters.ALPHANUMERIC + inputFilter = InputFilters.ALPHANUMERIC, ) AppTextField( @@ -1012,7 +1012,7 @@ fun HintSection( keyboardType = KeyboardType.Number, ), ), - inputFilter = InputFilters.DIGIT + inputFilter = InputFilters.DIGIT, ) AppSendButton( diff --git a/app/src/main/java/com/codenames/frontend/ui/screens/JoinLobbyScreen.kt b/app/src/main/java/com/codenames/frontend/ui/screens/JoinLobbyScreen.kt index 88b02ba..de0a256 100644 --- a/app/src/main/java/com/codenames/frontend/ui/screens/JoinLobbyScreen.kt +++ b/app/src/main/java/com/codenames/frontend/ui/screens/JoinLobbyScreen.kt @@ -145,7 +145,7 @@ fun JoinlobbyScreen( onDone = { submitJoin() }, ), ), - inputFilter = InputFilters.LOBBY_CODE + inputFilter = InputFilters.LOBBY_CODE, ) AppButton( diff --git a/app/src/main/java/com/codenames/frontend/ui/screens/UserNameScreen.kt b/app/src/main/java/com/codenames/frontend/ui/screens/UserNameScreen.kt index 14dddb9..4c6a080 100644 --- a/app/src/main/java/com/codenames/frontend/ui/screens/UserNameScreen.kt +++ b/app/src/main/java/com/codenames/frontend/ui/screens/UserNameScreen.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material3.AlertDialog import androidx.compose.material3.Text -import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState diff --git a/app/src/test/java/com/codenames/frontend/viewmodel/ChatViewModelTest.kt b/app/src/test/java/com/codenames/frontend/viewmodel/ChatViewModelTest.kt index 49bf459..48ea394 100644 --- a/app/src/test/java/com/codenames/frontend/viewmodel/ChatViewModelTest.kt +++ b/app/src/test/java/com/codenames/frontend/viewmodel/ChatViewModelTest.kt @@ -1,7 +1,6 @@ package com.codenames.frontend.viewmodel import com.codenames.frontend.data.model.ChatDomainModel -import com.codenames.frontend.data.model.ChatLists import com.codenames.frontend.data.model.enums.ChatTab import com.codenames.frontend.data.model.enums.Role import com.codenames.frontend.data.model.enums.Team @@ -340,14 +339,14 @@ class ChatViewModelTest { assertNull(viewModel.errorMessage.value) } + @Test fun testClearChatState() = runTest { - viewModel.cleanup() assertEquals(emptyList(), viewModel.chatState.value.lobbyMessages) assertEquals(emptyList(), viewModel.chatState.value.teamMessages) assertEquals(emptyList(), viewModel.chatState.value.operativeMessages) - } + } }