Simplified implementation of new chat text

This commit is contained in:
Juha Heinanen
2025-10-10 11:34:53 +03:00
parent 17db3e9572
commit 7f0f858033
2 changed files with 33 additions and 31 deletions
@@ -48,11 +48,11 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@@ -124,8 +124,6 @@ private fun ChatScreen(
val aor = account.aor val aor = account.aor
var chatMessages by remember(aor, peerUri) { mutableStateOf<List<Message>>(emptyList()) } var chatMessages by remember(aor, peerUri) { mutableStateOf<List<Message>>(emptyList()) }
val allDrafts by viewModel.aorPeerMessage.collectAsState()
val currentDraft = allDrafts.getOrDefault(AorPeer(aor, peerUri), TextFieldValue(""))
DisposableEffect(lifecycleOwner) { DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event -> val observer = LifecycleEventObserver { _, event ->
@@ -190,12 +188,9 @@ private fun ChatScreen(
bottomBar = { bottomBar = {
NewMessage( NewMessage(
ctx = ctx, ctx = ctx,
viewModel,
account = account, account = account,
peerUri = peerUri, peerUri = peerUri,
newMessage = currentDraft,
onNewMessageChange = { newTextFieldValue ->
viewModel.updateAorPeerMessage(aor, peerUri, newTextFieldValue)
},
addMessage = addMessage addMessage = addMessage
) )
}, },
@@ -481,16 +476,19 @@ private fun Messages(
@Composable @Composable
private fun NewMessage( private fun NewMessage(
ctx: Context, ctx: Context,
viewModel: ViewModel,
account: Account, account: Account,
peerUri: String, peerUri: String,
newMessage: TextFieldValue,
onNewMessageChange: (TextFieldValue) -> Unit,
addMessage: (message: Message) -> Unit addMessage: (message: Message) -> Unit
) { ) {
val aor = account.aor val aor = account.aor
val ua = UserAgent.ofAor(aor)!! val ua = UserAgent.ofAor(aor)!!
val newMessage = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(viewModel.getAorPeerMessage(aor, peerUri)))
}
var textFieldLoaded by remember { mutableStateOf(false) } var textFieldLoaded by remember { mutableStateOf(false) }
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
@@ -512,10 +510,11 @@ private fun NewMessage(
) { ) {
val keyboardController = LocalSoftwareKeyboardController.current val keyboardController = LocalSoftwareKeyboardController.current
OutlinedTextField( OutlinedTextField(
value = newMessage, value = newMessage.value,
placeholder = { Text(stringResource(R.string.new_message)) }, placeholder = { Text(stringResource(R.string.new_message)) },
onValueChange = { newValue -> onValueChange = {
onNewMessageChange(newValue) newMessage.value = it
viewModel.updateAorPeerMessage(aor, peerUri, it.text)
}, },
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
@@ -528,12 +527,13 @@ private fun NewMessage(
}, },
singleLine = false, singleLine = false,
trailingIcon = { trailingIcon = {
if (newMessage.text.isNotEmpty()) { if (newMessage.value.text.isNotEmpty()) {
Icon( Icon(
Icons.Outlined.Clear, Icons.Outlined.Clear,
contentDescription = "Clear", contentDescription = "Clear",
modifier = Modifier.clickable { modifier = Modifier.clickable {
onNewMessageChange(TextFieldValue("")) newMessage.value = TextFieldValue("")
viewModel.updateAorPeerMessage(aor, peerUri, "")
} }
) )
} }, } },
@@ -546,7 +546,7 @@ private fun NewMessage(
) )
) )
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if (newMessage.text.isNotEmpty()) if (newMessage.value.text.isNotEmpty())
focusRequester.requestFocus() focusRequester.requestFocus()
} }
Image( Image(
@@ -556,7 +556,7 @@ private fun NewMessage(
modifier = Modifier modifier = Modifier
.size(36.dp) .size(36.dp)
.clickable { .clickable {
val msgText = newMessage.text val msgText = newMessage.value.text
if (msgText.isNotEmpty()) { if (msgText.isNotEmpty()) {
keyboardController?.hide() keyboardController?.hide()
val time = System.currentTimeMillis() val time = System.currentTimeMillis()
@@ -595,7 +595,8 @@ private fun NewMessage(
msg.direction = MESSAGE_UP_FAIL msg.direction = MESSAGE_UP_FAIL
msg.responseReason = ctx.getString(R.string.message_failed) msg.responseReason = ctx.getString(R.string.message_failed)
} else { } else {
onNewMessageChange(TextFieldValue("")) newMessage.value = TextFieldValue("")
viewModel.updateAorPeerMessage(aor, peerUri, "")
keyboardController?.hide() keyboardController?.hide()
} }
} }
@@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.runtime.mutableIntStateOf
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
@@ -31,32 +31,33 @@ class ViewModel(application: Application) : AndroidViewModel(application) {
_selectedAor.value = newValue _selectedAor.value = newValue
} }
private val _aorPeerMessage = MutableStateFlow(mutableMapOf<AorPeer, TextFieldValue>()) private val _aorPeerMessage = MutableStateFlow(mutableMapOf<AorPeer, String>())
val aorPeerMessage: StateFlow<Map<AorPeer, TextFieldValue>> = _aorPeerMessage.asStateFlow()
fun updateAorPeerMessage(aor: String, peerUri: String, value: TextFieldValue) { fun updateAorPeerMessage(aor: String, peerUri: String, message: String) {
val key = AorPeer(aor, peerUri) val key = AorPeer(aor, peerUri)
val newMap = _aorPeerMessage.value.toMutableMap() if (message == "")
_aorPeerMessage.value.remove(key)
if (value.text.isEmpty())
newMap.remove(key)
else else
newMap[key] = value _aorPeerMessage.value[key] = message
_aorPeerMessage.value = newMap
} }
private val _showKeyboard = mutableStateOf(0) fun getAorPeerMessage(aor: String, peerUri: String): String {
val key = AorPeer(aor, peerUri)
return _aorPeerMessage.value.getOrDefault(key, "")
}
private val _showKeyboard = mutableIntStateOf(0)
val showKeyboard: State<Int> = _showKeyboard val showKeyboard: State<Int> = _showKeyboard
fun requestShowKeyboard() { fun requestShowKeyboard() {
_showKeyboard.value++ _showKeyboard.intValue++
} }
private val _hideKeyboard = mutableStateOf(0) private val _hideKeyboard = mutableIntStateOf(0)
val hideKeyboard: State<Int> = _hideKeyboard val hideKeyboard: State<Int> = _hideKeyboard
fun requestHideKeyboard() { fun requestHideKeyboard() {
_hideKeyboard.value++ _hideKeyboard.intValue++
} }
private val _speakerIcon = MutableStateFlow(R.drawable.speaker_off) private val _speakerIcon = MutableStateFlow(R.drawable.speaker_off)