mobile: disable ability to write text in text view when sending a message and prevent saving message that is in progress as draft (#2371)

* android: prevent saving message that is in progress as draft

* android: disable ability to write text in text view when sending a message

* ios: prevent saving message that is in progress as draft

* ios: disable ability to write text in text view when sending a message

* do not show welcome button when not needed
This commit is contained in:
Stanislav Dmitrenko
2023-05-03 15:57:10 +03:00
committed by GitHub
parent 5af389ae3f
commit 69767126aa
8 changed files with 68 additions and 30 deletions
@@ -118,7 +118,7 @@ data class ComposeState(
val attachmentDisabled: Boolean
get() {
if (editing || liveMessage != null) return true
if (editing || liveMessage != null || inProgress) return true
return when (preview) {
ComposePreview.NoPreview -> false
is ComposePreview.CLinkPreview -> false
@@ -623,23 +623,27 @@ fun ComposeView(
fun previewView() {
when (val preview = composeState.value.preview) {
ComposePreview.NoPreview -> {}
is ComposePreview.CLinkPreview -> ComposeLinkView(preview.linkPreview, ::cancelLinkPreview)
is ComposePreview.CLinkPreview -> ComposeLinkView(
preview.linkPreview,
::cancelLinkPreview,
cancelEnabled = !composeState.value.inProgress
)
is ComposePreview.MediaPreview -> ComposeImageView(
preview,
::cancelImages,
cancelEnabled = !composeState.value.editing
cancelEnabled = !composeState.value.editing && !composeState.value.inProgress
)
is ComposePreview.VoicePreview -> ComposeVoiceView(
preview.voice,
preview.durationMs,
preview.finished,
cancelEnabled = !composeState.value.editing,
cancelEnabled = !composeState.value.editing && !composeState.value.inProgress,
::cancelVoice
)
is ComposePreview.FilePreview -> ComposeFileView(
preview.fileName,
::cancelFile,
cancelEnabled = !composeState.value.editing
cancelEnabled = !composeState.value.editing && !composeState.value.inProgress
)
}
}
@@ -657,6 +661,14 @@ fun ComposeView(
}
}
// In case a user sent something, state is in progress, the user rotates a screen to different orientation.
// Without clearing the state the user will be unable to send anything until re-enters ChatView
LaunchedEffect(Unit) {
if (composeState.value.inProgress) {
clearState()
}
}
LaunchedEffect(chatModel.sharedContent.value) {
// Important. If it's null, don't do anything, chat is not closed yet but will be after a moment
if (chatModel.chatId.value == null) return@LaunchedEffect
@@ -744,6 +756,8 @@ fun ComposeView(
resetLinkPreview()
clearCurrentDraft()
deleteUnusedFiles()
} else if (composeState.value.inProgress) {
clearCurrentDraft()
} else if (!composeState.value.empty) {
if (cs.preview is ComposePreview.VoicePreview && !cs.preview.finished) {
composeState.value = cs.copy(preview = cs.preview.copy(finished = true))
@@ -79,7 +79,7 @@ fun SendMsgView(
val showDeleteTextButton = rememberSaveable { mutableStateOf(false) }
NativeKeyboard(composeState, textStyle, showDeleteTextButton, userIsObserver, onMessageChange)
// Disable clicks on text field
if (cs.preview is ComposePreview.VoicePreview || !userCanSend) {
if (cs.preview is ComposePreview.VoicePreview || !userCanSend || cs.inProgress) {
Box(Modifier
.matchParentSize()
.clickable(enabled = !userCanSend, indication = null, interactionSource = remember { MutableInteractionSource() }, onClick = {
@@ -250,14 +250,20 @@ private fun NativeKeyboard(
Log.e(chat.simplex.app.TAG, e.stackTraceToString())
}
}
editText.doOnTextChanged { text, _, _, _ -> onMessageChange(text.toString()) }
editText.doOnTextChanged { text, _, _, _ ->
if (!composeState.value.inProgress) {
onMessageChange(text.toString())
} else if (text.toString() != composeState.value.message) {
editText.setText(composeState.value.message)
}
}
editText.doAfterTextChanged { text -> if (composeState.value.preview is ComposePreview.VoicePreview && text.toString() != "") editText.setText("") }
editText
}) {
it.setTextColor(textColor.toArgb())
it.textSize = textStyle.value.fontSize.value
DrawableCompat.setTint(it.background, tintColor.toArgb())
it.isFocusable = composeState.value.preview !is ComposePreview.VoicePreview
it.isFocusable = composeState.value.preview !is ComposePreview.VoicePreview && !cs.inProgress
it.isFocusableInTouchMode = it.isFocusable
if (cs.message != it.text.toString()) {
it.setText(cs.message)
@@ -270,7 +276,7 @@ private fun NativeKeyboard(
imm.showSoftInput(it, InputMethodManager.SHOW_IMPLICIT)
showKeyboard = false
}
showDeleteTextButton.value = it.lineCount >= 4
showDeleteTextButton.value = it.lineCount >= 4 && !cs.inProgress
}
if (composeState.value.preview is ComposePreview.VoicePreview) {
ComposeOverlay(R.string.voice_message_send_text, textStyle, padding)
@@ -77,7 +77,7 @@ suspend fun getLinkPreview(url: String): LinkPreview? {
}
@Composable
fun ComposeLinkView(linkPreview: LinkPreview?, cancelPreview: () -> Unit) {
fun ComposeLinkView(linkPreview: LinkPreview?, cancelPreview: () -> Unit, cancelEnabled: Boolean) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
Row(
Modifier.fillMaxWidth().padding(top = 8.dp).background(sentColor),
@@ -109,13 +109,15 @@ fun ComposeLinkView(linkPreview: LinkPreview?, cancelPreview: () -> Unit) {
)
}
}
IconButton(onClick = cancelPreview, modifier = Modifier.padding(0.dp)) {
Icon(
painterResource(R.drawable.ic_close),
contentDescription = stringResource(R.string.icon_descr_cancel_link_preview),
tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(10.dp)
)
if (cancelEnabled) {
IconButton(onClick = cancelPreview, modifier = Modifier.padding(0.dp)) {
Icon(
painterResource(R.drawable.ic_close),
contentDescription = stringResource(R.string.icon_descr_cancel_link_preview),
tint = MaterialTheme.colors.primary,
modifier = Modifier.padding(10.dp)
)
}
}
}
}
@@ -193,7 +195,7 @@ fun PreviewChatItemLinkView() {
@Composable
fun PreviewComposeLinkView() {
SimpleXTheme {
ComposeLinkView(LinkPreview.sampleData) { -> }
ComposeLinkView(LinkPreview.sampleData, cancelPreview = { -> }, true)
}
}
@@ -201,6 +203,6 @@ fun PreviewComposeLinkView() {
@Composable
fun PreviewComposeLinkViewLoading() {
SimpleXTheme {
ComposeLinkView(null) { -> }
ComposeLinkView(null, cancelPreview = { -> }, true)
}
}
File diff suppressed because one or more lines are too long
@@ -154,7 +154,7 @@ struct ComposeState {
}
var attachmentDisabled: Bool {
if editing || liveMessage != nil { return true }
if editing || liveMessage != nil || inProgress { return true }
switch preview {
case .noPreview: return false
case .linkPreview: return false
@@ -427,6 +427,8 @@ struct ComposeView: View {
clearCurrentDraft()
sendMessage()
resetLinkPreview()
} else if (composeState.inProgress) {
clearCurrentDraft()
} else if !composeState.empty {
saveCurrentDraft()
} else {
@@ -522,7 +524,11 @@ struct ComposeView: View {
case .noPreview:
EmptyView()
case let .linkPreview(linkPreview: preview):
ComposeLinkView(linkPreview: preview, cancelPreview: cancelLinkPreview)
ComposeLinkView(
linkPreview: preview,
cancelPreview: cancelLinkPreview,
cancelEnabled: !composeState.inProgress
)
case let .mediaPreviews(mediaPreviews: media):
ComposeImageView(
images: media.map { (img, _) in img },
@@ -530,7 +536,7 @@ struct ComposeView: View {
composeState = composeState.copy(preview: .noPreview)
chosenMedia = []
},
cancelEnabled: !composeState.editing)
cancelEnabled: !composeState.editing && !composeState.inProgress)
case let .voicePreview(recordingFileName, _):
ComposeVoiceView(
recordingFileName: recordingFileName,
@@ -540,7 +546,7 @@ struct ComposeView: View {
cancelVoiceMessageRecording($0)
clearState()
},
cancelEnabled: !composeState.editing,
cancelEnabled: !composeState.editing && !composeState.inProgress,
stopPlayback: $stopPlayback
)
case let .filePreview(fileName, _):
@@ -549,7 +555,7 @@ struct ComposeView: View {
cancelFile: {
composeState = composeState.copy(preview: .noPreview)
},
cancelEnabled: !composeState.editing)
cancelEnabled: !composeState.editing && !composeState.inProgress)
}
}
@@ -13,6 +13,7 @@ import PhotosUI
struct NativeTextEditor: UIViewRepresentable {
@Binding var text: String
@Binding var disableEditing: Bool
let height: CGFloat
let font: UIFont
@FocusState.Binding var focused: Bool
@@ -26,7 +27,11 @@ struct NativeTextEditor: UIViewRepresentable {
field.textAlignment = alignment == .leading ? .left : .right
field.autocapitalizationType = .sentences
field.setOnTextChangedListener { newText, images in
text = newText
if !disableEditing {
text = newText
} else {
field.text = text
}
if !images.isEmpty {
onImagesAdded(images)
}
@@ -140,6 +145,7 @@ struct NativeTextEditor_Previews: PreviewProvider{
@FocusState var keyboardVisible: Bool
return NativeTextEditor(
text: Binding.constant("Hello, world!"),
disableEditing: Binding.constant(false),
height: 100,
font: UIFont.preferredFont(forTextStyle: .body),
focused: $keyboardVisible,
@@ -65,6 +65,7 @@ struct SendMessageView: View {
NativeTextEditor(
text: $composeState.message,
disableEditing: $composeState.inProgress,
height: teHeight,
font: teUiFont,
focused: $keyboardVisible,
@@ -83,7 +84,7 @@ struct SendMessageView: View {
.padding([.bottom, .trailing], 3)
} else {
VStack(alignment: .trailing) {
if teHeight > 100 {
if teHeight > 100 && !composeState.inProgress {
deleteTextButton()
Spacer()
}
@@ -46,7 +46,9 @@ struct GroupChatInfoView: View {
if groupInfo.canEdit {
editGroupButton()
}
addOrEditWelcomeMessage()
if groupInfo.groupProfile.description != nil || groupInfo.canEdit {
addOrEditWelcomeMessage()
}
groupPreferencesButton($groupInfo)
} header: {
Text("")