Don't loose content of new message that has not been sent yet
This commit is contained in:
@@ -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,6 +124,8 @@ 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 ->
|
||||||
@@ -185,10 +187,27 @@ private fun ChatScreen(
|
|||||||
TopAppBar(ctx, navController, viewModel, account, peerUri)
|
TopAppBar(ctx, navController, viewModel, account, peerUri)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bottomBar = { NewMessage(ctx, account, peerUri, addMessage) },
|
bottomBar = {
|
||||||
|
NewMessage(
|
||||||
|
ctx = ctx,
|
||||||
|
account = account,
|
||||||
|
peerUri = peerUri,
|
||||||
|
newMessage = currentDraft,
|
||||||
|
onNewMessageChange = { newTextFieldValue ->
|
||||||
|
viewModel.updateAorPeerMessage(aor, peerUri, newTextFieldValue)
|
||||||
|
},
|
||||||
|
addMessage = addMessage
|
||||||
|
)
|
||||||
|
},
|
||||||
content = { contentPadding ->
|
content = { contentPadding ->
|
||||||
if (areMessagesLoaded)
|
if (areMessagesLoaded)
|
||||||
ChatContent(ctx, navController, contentPadding, account, peerUri, chatMessages, reloadMessages)
|
ChatContent(ctx,
|
||||||
|
navController,
|
||||||
|
contentPadding,
|
||||||
|
account, peerUri,
|
||||||
|
chatMessages,
|
||||||
|
reloadMessages
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -464,15 +483,14 @@ private fun NewMessage(
|
|||||||
ctx: Context,
|
ctx: Context,
|
||||||
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(""))
|
|
||||||
}
|
|
||||||
var textFieldLoaded by remember { mutableStateOf(false) }
|
var textFieldLoaded by remember { mutableStateOf(false) }
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
@@ -494,9 +512,11 @@ private fun NewMessage(
|
|||||||
) {
|
) {
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = newMessage.value,
|
value = newMessage,
|
||||||
placeholder = { Text(stringResource(R.string.new_message)) },
|
placeholder = { Text(stringResource(R.string.new_message)) },
|
||||||
onValueChange = { newMessage.value = it },
|
onValueChange = { newValue ->
|
||||||
|
onNewMessageChange(newValue)
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(end = 8.dp)
|
.padding(end = 8.dp)
|
||||||
@@ -508,11 +528,13 @@ private fun NewMessage(
|
|||||||
},
|
},
|
||||||
singleLine = false,
|
singleLine = false,
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
if (newMessage.value.text.isNotEmpty()) {
|
if (newMessage.text.isNotEmpty()) {
|
||||||
Icon(
|
Icon(
|
||||||
Icons.Outlined.Clear,
|
Icons.Outlined.Clear,
|
||||||
contentDescription = "Clear",
|
contentDescription = "Clear",
|
||||||
modifier = Modifier.clickable { newMessage.value = TextFieldValue("") }
|
modifier = Modifier.clickable {
|
||||||
|
onNewMessageChange(TextFieldValue(""))
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} },
|
} },
|
||||||
label = { LabelText(stringResource(R.string.new_message)) },
|
label = { LabelText(stringResource(R.string.new_message)) },
|
||||||
@@ -524,7 +546,7 @@ private fun NewMessage(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (newMessage.value.text.isNotEmpty())
|
if (newMessage.text.isNotEmpty())
|
||||||
focusRequester.requestFocus()
|
focusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
Image(
|
Image(
|
||||||
@@ -534,12 +556,20 @@ private fun NewMessage(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(36.dp)
|
.size(36.dp)
|
||||||
.clickable {
|
.clickable {
|
||||||
val msgText = newMessage.value.text
|
val msgText = newMessage.text
|
||||||
if (msgText.isNotEmpty()) {
|
if (msgText.isNotEmpty()) {
|
||||||
keyboardController?.hide()
|
keyboardController?.hide()
|
||||||
val time = System.currentTimeMillis()
|
val time = System.currentTimeMillis()
|
||||||
val msg =
|
val msg = Message(
|
||||||
Message(aor, peerUri, msgText, time, MESSAGE_UP_WAIT, 0, "", true)
|
aor,
|
||||||
|
peerUri,
|
||||||
|
msgText,
|
||||||
|
time,
|
||||||
|
MESSAGE_UP_WAIT,
|
||||||
|
0,
|
||||||
|
"",
|
||||||
|
true
|
||||||
|
)
|
||||||
msg.add()
|
msg.add()
|
||||||
var msgUri = ""
|
var msgUri = ""
|
||||||
addMessage(msg)
|
addMessage(msg)
|
||||||
@@ -565,7 +595,7 @@ 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 {
|
||||||
newMessage.value = TextFieldValue("")
|
onNewMessageChange(TextFieldValue(""))
|
||||||
keyboardController?.hide()
|
keyboardController?.hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +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.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
|
||||||
@@ -19,6 +20,8 @@ sealed class NavigationCommand {
|
|||||||
// Add other navigation commands as needed
|
// Add other navigation commands as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class AorPeer(val aor: String, val peer: String)
|
||||||
|
|
||||||
class ViewModel(application: Application) : AndroidViewModel(application) {
|
class ViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
private val _selectedAor = MutableStateFlow("")
|
private val _selectedAor = MutableStateFlow("")
|
||||||
@@ -28,6 +31,20 @@ class ViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
_selectedAor.value = newValue
|
_selectedAor.value = newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val _aorPeerMessage = MutableStateFlow(mutableMapOf<AorPeer, TextFieldValue>())
|
||||||
|
val aorPeerMessage: StateFlow<Map<AorPeer, TextFieldValue>> = _aorPeerMessage.asStateFlow()
|
||||||
|
|
||||||
|
fun updateAorPeerMessage(aor: String, peerUri: String, value: TextFieldValue) {
|
||||||
|
val key = AorPeer(aor, peerUri)
|
||||||
|
val newMap = _aorPeerMessage.value.toMutableMap()
|
||||||
|
|
||||||
|
if (value.text.isEmpty())
|
||||||
|
newMap.remove(key)
|
||||||
|
else
|
||||||
|
newMap[key] = value
|
||||||
|
_aorPeerMessage.value = newMap
|
||||||
|
}
|
||||||
|
|
||||||
private val _speakerIcon = MutableStateFlow(R.drawable.speaker_off)
|
private val _speakerIcon = MutableStateFlow(R.drawable.speaker_off)
|
||||||
val speakerIcon: StateFlow<Int> = _speakerIcon.asStateFlow()
|
val speakerIcon: StateFlow<Int> = _speakerIcon.asStateFlow()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user