From e14be2d187128803af563ed426af1838c1e6a0e3 Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Sat, 5 Sep 2026 20:20:35 +0200 Subject: [PATCH] refactor: pick an artifact's dialect, do not invent one while adding it Adding an artifact offered a "New dialect" chip that swapped in three more fields -- name, country, language -- and minted a dialect on the way to creating the artifact. Now that a group defines its dialects on its own screen, that path is a second, worse way to do the same thing: it creates a dialect as a side effect of an unrelated action, in a form where the fields belong to neither entity clearly, and with no sight of what the group has already defined beyond a row of chips. Drop it. The chip row is now exactly the dialects that exist, and selectedDialectId changes meaning from "null = create a new one" to "null = nothing picked yet" -- which the FAB rejects alongside the other required fields, rather than falling through to creating something. A group with no dialects yet gets a line saying so and pointing at the group screen, instead of a lone chip that opens a form. addArtifact loses the three TextFieldStates and existingDialectId for a single dialectId, and with them the branch that called addDialect and threaded its id back in. Validation is now one condition rather than one per path. Co-Authored-By: Claude Opus 5 --- .../ui/composable/AddArtifactScreen.kt | 137 +++--------------- .../ui/view/model/AddArtifactViewModel.kt | 34 +---- 2 files changed, 24 insertions(+), 147 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/composable/AddArtifactScreen.kt b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/composable/AddArtifactScreen.kt index 38d68b94..34e7932f 100644 --- a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/composable/AddArtifactScreen.kt +++ b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/composable/AddArtifactScreen.kt @@ -18,9 +18,6 @@ import androidx.compose.material.icons.filled.Label import androidx.compose.material.icons.filled.Link import androidx.compose.material.icons.filled.LocalOffer import androidx.compose.material.icons.filled.MoreVert -import androidx.compose.material.icons.filled.Public -import androidx.compose.material.icons.filled.Title -import androidx.compose.material.icons.filled.Translate import androidx.compose.material3.BottomAppBar import androidx.compose.material3.BottomAppBarDefaults import androidx.compose.material3.ExperimentalMaterial3Api @@ -108,12 +105,9 @@ fun AddArtifactScreen( val nameFieldState = rememberTextFieldState() val urlFieldState = rememberTextFieldState() val versionLabelFieldState = rememberTextFieldState("1.0") - val dialectNameFieldState = rememberTextFieldState() - val dialectCountryFieldState = rememberTextFieldState() - val dialectLanguageFieldState = rememberTextFieldState() - // null = "New dialect" (show the create fields); otherwise the id of - // an existing dialect to reuse. + // The id of the group dialect this artifact is written in. Null until + // one is picked; dialects are defined from the group detail screen. var selectedDialectId: String? by remember { mutableStateOf(null) } Scaffold( @@ -138,10 +132,7 @@ fun AddArtifactScreen( nameField = nameFieldState, urlField = urlFieldState, versionLabelField = versionLabelFieldState, - existingDialectId = selectedDialectId, - dialectNameField = dialectNameFieldState, - dialectCountryField = dialectCountryFieldState, - dialectLanguageField = dialectLanguageFieldState, + dialectId = selectedDialectId, onSuccess = { artifactId -> // Open the newly created artifact, removing this // add screen from the back stack. @@ -266,112 +257,24 @@ fun AddArtifactScreen( Text("Source dialect") - FlowRow( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - addArtifactUIState.dialects.forEach { dialect -> - FilterChip( - selected = selectedDialectId == dialect.id, - onClick = { selectedDialectId = dialect.id }, - label = { Text(dialect.name) } - ) + if (addArtifactUIState.dialects.isEmpty()) { + Text( + text = "No dialects have been defined in this group yet. Add one from the group's detail screen first.", + style = MaterialTheme.typography.bodySmall + ) + } else { + FlowRow( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + addArtifactUIState.dialects.forEach { dialect -> + FilterChip( + selected = selectedDialectId == dialect.id, + onClick = { selectedDialectId = dialect.id }, + label = { Text(dialect.name) } + ) + } } - FilterChip( - selected = selectedDialectId == null, - onClick = { selectedDialectId = null }, - leadingIcon = { - Icon( - Icons.Default.Add, - contentDescription = "Create a new dialect" - ) - }, - label = { Text("New dialect") } - ) - } - - // Only collect new-dialect details when not reusing an existing one. - if (selectedDialectId == null) { - OutlinedTextField( - modifier = Modifier.fillMaxWidth() - .background(BottomAppBarDefaults.containerColor), - state = dialectNameFieldState, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = Color.Transparent, - unfocusedBorderColor = Color.Transparent, - disabledBorderColor = Color.Transparent - ), - leadingIcon = { - Icon( - Icons.Default.Title, - contentDescription = "Name of the dialect" - ) - }, - label = { - Text( - text = "Dialect Name" - ) - }, - placeholder = { - Text( - text = "eg. Sesotho" - ) - }, - ) - - OutlinedTextField( - modifier = Modifier.fillMaxWidth() - .background(BottomAppBarDefaults.containerColor), - state = dialectCountryFieldState, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = Color.Transparent, - unfocusedBorderColor = Color.Transparent, - disabledBorderColor = Color.Transparent - ), - leadingIcon = { - Icon( - Icons.Default.Public, - contentDescription = "Country of the dialect" - ) - }, - label = { - Text( - text = "Country" - ) - }, - placeholder = { - Text( - text = "eg. Lesotho" - ) - }, - ) - - OutlinedTextField( - modifier = Modifier.fillMaxWidth() - .background(BottomAppBarDefaults.containerColor), - state = dialectLanguageFieldState, - colors = OutlinedTextFieldDefaults.colors( - focusedBorderColor = Color.Transparent, - unfocusedBorderColor = Color.Transparent, - disabledBorderColor = Color.Transparent - ), - leadingIcon = { - Icon( - Icons.Default.Translate, - contentDescription = "Language of the dialect" - ) - }, - label = { - Text( - text = "Language" - ) - }, - placeholder = { - Text( - text = "eg. st" - ) - }, - ) } // TODO: Add Visibility diff --git a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/AddArtifactViewModel.kt b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/AddArtifactViewModel.kt index a84ec5a8..cc90e468 100644 --- a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/AddArtifactViewModel.kt +++ b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/AddArtifactViewModel.kt @@ -62,10 +62,7 @@ class AddArtifactViewModel( nameField: TextFieldState, urlField: TextFieldState, versionLabelField: TextFieldState, - existingDialectId: HexKey?, - dialectNameField: TextFieldState, - dialectCountryField: TextFieldState, - dialectLanguageField: TextFieldState, + dialectId: HexKey?, visibility: String = MantraRepository.DEFAULT_VISIBILITY, license: String = MantraRepository.DEFAULT_LICENSE, onSuccess: (artifactId: String) -> Unit, @@ -75,16 +72,10 @@ class AddArtifactViewModel( val name = nameField.text.toString() val url = urlField.text.toString() val versionLabel = versionLabelField.text.toString() - val dialectName = dialectNameField.text.toString() - val dialectCountry = dialectCountryField.text.toString() - val dialectLanguage = dialectLanguageField.text.toString() - // When no existing dialect is selected, the new-dialect fields are required. - val creatingNewDialect = existingDialectId.isNullOrBlank() - val newDialectIncomplete = dialectName.isBlank() || dialectCountry.isBlank() || dialectLanguage.isBlank() - if (name.isBlank() || url.isBlank() || versionLabel.isBlank() || - (creatingNewDialect && newDialectIncomplete) - ) { + // addArtifact requires an existing source dialect; they are defined + // from the group detail screen, not here. + if (name.isBlank() || url.isBlank() || versionLabel.isBlank() || dialectId.isNullOrBlank()) { onFailure.invoke() return } @@ -95,20 +86,6 @@ class AddArtifactViewModel( viewModelScope.launch(Dispatchers.IO) { val artifactInnerEvent = runCatching { - // Reuse the selected dialect, or create a new source dialect and - // reference it by id. addArtifact requires a valid dialectId. - val dialectId = if (creatingNewDialect) { - mantraRepository.addDialect( - localChatRoom = localChatRoom, - name = dialectName, - country = dialectCountry, - language = dialectLanguage, - userPublicKey = activeUserPublicKey, - )?.id ?: return@runCatching null - } else { - existingDialectId - } - mantraRepository.addArtifact( localChatRoom = localChatRoom, name = name, @@ -127,9 +104,6 @@ class AddArtifactViewModel( nameField.clearText() urlField.clearText() versionLabelField.clearText() - dialectNameField.clearText() - dialectCountryField.clearText() - dialectLanguageField.clearText() viewModelScope.launch(Dispatchers.Main) { onSuccess.invoke(artifactInnerEvent.id)