diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt index 7840c307c7..49d3015c05 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt @@ -43,6 +43,7 @@ import io.element.android.features.messages.impl.timeline.components.customreact import io.element.android.features.messages.impl.timeline.components.reactionsummary.ReactionSummaryState import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetState import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentWithAttachment import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent @@ -273,6 +274,9 @@ class MessagesPresenter @AssistedInject constructor( TimelineItemAction.CopyLink -> handleCopyLink(targetEvent) TimelineItemAction.Redact -> handleActionRedact(targetEvent) TimelineItemAction.Edit -> handleActionEdit(targetEvent, composerState, enableTextFormatting) + TimelineItemAction.AddCaption -> handleActionAddCaption(targetEvent, composerState) + TimelineItemAction.EditCaption -> handleActionEditCaption(targetEvent, composerState) + TimelineItemAction.RemoveCaption -> handleRemoveCaption(targetEvent) TimelineItemAction.Reply, TimelineItemAction.ReplyInThread -> handleActionReply(targetEvent, composerState, timelineProtectionState) TimelineItemAction.ViewSource -> handleShowDebugInfoAction(targetEvent) @@ -285,6 +289,16 @@ class MessagesPresenter @AssistedInject constructor( } } + private suspend fun handleRemoveCaption(targetEvent: TimelineItem.Event) { + timelineController.invokeOnCurrentTimeline { + editCaption( + eventOrTransactionId = targetEvent.eventOrTransactionId, + caption = null, + formattedCaption = null, + ) + } + } + private suspend fun handlePinAction(targetEvent: TimelineItem.Event) { if (targetEvent.eventId == null) return analyticsService.capture( @@ -387,6 +401,32 @@ class MessagesPresenter @AssistedInject constructor( } } + private fun handleActionAddCaption( + targetEvent: TimelineItem.Event, + composerState: MessageComposerState, + ) { + val composerMode = MessageComposerMode.EditCaption( + eventOrTransactionId = targetEvent.eventOrTransactionId, + content = "", + ) + composerState.eventSink( + MessageComposerEvents.SetMode(composerMode) + ) + } + + private fun handleActionEditCaption( + targetEvent: TimelineItem.Event, + composerState: MessageComposerState, + ) { + val composerMode = MessageComposerMode.EditCaption( + eventOrTransactionId = targetEvent.eventOrTransactionId, + content = (targetEvent.content as? TimelineItemEventContentWithAttachment)?.caption.orEmpty(), + ) + composerState.eventSink( + MessageComposerEvents.SetMode(composerMode) + ) + } + private suspend fun handleActionReply( targetEvent: TimelineItem.Event, composerState: MessageComposerState, diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt index 39e6cd8836..7bd4668ed4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt @@ -27,6 +27,7 @@ import io.element.android.features.messages.impl.crypto.sendfailure.VerifiedUser import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemCallNotifyContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentWithAttachment import io.element.android.features.messages.impl.timeline.model.event.TimelineItemLegacyCallInviteContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemPollContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent @@ -154,6 +155,16 @@ class DefaultActionListPresenter @AssistedInject constructor( } if (timelineItem.isEditable) { add(TimelineItemAction.Edit) + } else { + // Caption + if (timelineItem.isMine && timelineItem.content is TimelineItemEventContentWithAttachment) { + if (timelineItem.content.caption == null) { + add(TimelineItemAction.AddCaption) + } else { + add(TimelineItemAction.EditCaption) + add(TimelineItemAction.RemoveCaption) + } + } } if (canRedact && timelineItem.content is TimelineItemPollContent && !timelineItem.content.isEnded) { add(TimelineItemAction.EndPoll) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt index cddf3ff8e4..979f041297 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/model/TimelineItemAction.kt @@ -28,6 +28,9 @@ sealed class TimelineItemAction( data object Reply : TimelineItemAction(CommonStrings.action_reply, CompoundDrawables.ic_compound_reply) data object ReplyInThread : TimelineItemAction(CommonStrings.action_reply_in_thread, CompoundDrawables.ic_compound_reply) data object Edit : TimelineItemAction(CommonStrings.action_edit, CompoundDrawables.ic_compound_edit) + data object EditCaption : TimelineItemAction(CommonStrings.action_edit_caption, CompoundDrawables.ic_compound_edit) + data object AddCaption : TimelineItemAction(CommonStrings.action_add_caption, CompoundDrawables.ic_compound_edit) + data object RemoveCaption : TimelineItemAction(CommonStrings.action_remove_caption, CompoundDrawables.ic_compound_delete, destructive = true) data object ViewSource : TimelineItemAction(CommonStrings.action_view_source, CommonDrawables.ic_developer_options) data object ReportContent : TimelineItemAction(CommonStrings.action_report_content, CompoundDrawables.ic_compound_chat_problem, destructive = true) data object EndPoll : TimelineItemAction(CommonStrings.action_end_poll, CompoundDrawables.ic_compound_polls_end) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index 7ad64c21d0..ebccc8dff4 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -254,7 +254,7 @@ class MessageComposerPresenter @Inject constructor( when (event) { MessageComposerEvents.ToggleFullScreenState -> isFullScreen.value = !isFullScreen.value MessageComposerEvents.CloseSpecialMode -> { - if (messageComposerContext.composerMode is MessageComposerMode.Edit) { + if (messageComposerContext.composerMode.isEditing) { localCoroutineScope.launch { resetComposer(markdownTextEditorState, richTextEditorState, fromEdit = true) } @@ -431,7 +431,15 @@ class MessageComposerPresenter @Inject constructor( } } } - + is MessageComposerMode.EditCaption -> { + timelineController.invokeOnCurrentTimeline { + editCaption( + capturedMode.eventOrTransactionId, + caption = message.markdown, + formattedCaption = message.html + ) + } + } is MessageComposerMode.Reply -> { timelineController.invokeOnCurrentTimeline { replyMessage(capturedMode.eventId, message.markdown, message.html, message.intentionalMentions) @@ -570,6 +578,10 @@ class MessageComposerPresenter @Inject constructor( mode.eventOrTransactionId.eventId?.let { eventId -> ComposerDraftType.Edit(eventId) } } is MessageComposerMode.Reply -> ComposerDraftType.Reply(mode.eventId) + is MessageComposerMode.EditCaption -> { + // TODO Need a new type to save caption in the SDK + null + } } return if (draftType == null || message.markdown.isBlank()) { null @@ -644,7 +656,14 @@ class MessageComposerPresenter @Inject constructor( val currentComposerMode = messageComposerContext.composerMode when (newComposerMode) { is MessageComposerMode.Edit -> { - if (currentComposerMode !is MessageComposerMode.Edit) { + if (currentComposerMode.isEditing.not()) { + val draft = createDraftFromState(markdownTextEditorState, richTextEditorState) + updateDraft(draft, isVolatile = true).join() + } + setText(newComposerMode.content, markdownTextEditorState, richTextEditorState) + } + is MessageComposerMode.EditCaption -> { + if (currentComposerMode.isEditing.not()) { val draft = createDraftFromState(markdownTextEditorState, richTextEditorState) updateDraft(draft, isVolatile = true).join() } @@ -652,7 +671,7 @@ class MessageComposerPresenter @Inject constructor( } else -> { // When coming from edit, just clear the composer as it'd be weird to reset a volatile draft in this scenario. - if (currentComposerMode is MessageComposerMode.Edit) { + if (currentComposerMode.isEditing) { setText("", markdownTextEditorState, richTextEditorState) } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt index 2b7c121dec..00f7a9a17c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt @@ -59,10 +59,17 @@ interface Timeline : AutoCloseable { suspend fun editMessage( eventOrTransactionId: EventOrTransactionId, - body: String, htmlBody: String?, + body: String, + htmlBody: String?, intentionalMentions: List, ): Result + suspend fun editCaption( + eventOrTransactionId: EventOrTransactionId, + caption: String?, + formattedCaption: String?, + ): Result + suspend fun replyMessage( eventId: EventId, body: String, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index 813cc9c7cb..200f1289b4 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -295,22 +295,40 @@ class RustTimeline( body: String, htmlBody: String?, intentionalMentions: List, - ): Result = - withContext(dispatcher) { - runCatching { - val editedContent = EditedContent.RoomMessage( - content = MessageEventContent.from( - body = body, - htmlBody = htmlBody, - intentionalMentions = intentionalMentions - ), - ) - inner.edit( - newContent = editedContent, - eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(), - ) - } + ): Result = withContext(dispatcher) { + runCatching { + val editedContent = EditedContent.RoomMessage( + content = MessageEventContent.from( + body = body, + htmlBody = htmlBody, + intentionalMentions = intentionalMentions + ), + ) + inner.edit( + newContent = editedContent, + eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(), + ) } + } + + override suspend fun editCaption( + eventOrTransactionId: EventOrTransactionId, + caption: String?, + formattedCaption: String?, + ): Result = withContext(dispatcher) { + runCatching { + val editedContent = EditedContent.MediaCaption( + caption = caption, + formattedCaption = formattedCaption?.let { + FormattedBody(body = it, format = MessageFormat.Html) + }, + ) + inner.edit( + newContent = editedContent, + eventOrTransactionId = eventOrTransactionId.toRustEventOrTransactionId(), + ) + } + } override suspend fun replyMessage( eventId: EventId, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt index f96815c85a..6edfd22c50 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt @@ -92,6 +92,24 @@ class FakeTimeline( intentionalMentions ) + var editCaptionLambda: ( + eventOrTransactionId: EventOrTransactionId, + caption: String?, + formattedCaption: String?, + ) -> Result = { _, _, _ -> + lambdaError() + } + + override suspend fun editCaption( + eventOrTransactionId: EventOrTransactionId, + caption: String?, + formattedCaption: String?, + ): Result = editCaptionLambda( + eventOrTransactionId, + caption, + formattedCaption, + ) + var replyMessageLambda: ( eventId: EventId, body: String, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ComposerModeView.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ComposerModeView.kt index 218a8653cf..eaa5669206 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ComposerModeView.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/ComposerModeView.kt @@ -47,6 +47,16 @@ internal fun ComposerModeView( when (composerMode) { is MessageComposerMode.Edit -> { EditingModeView( + text = stringResource(CommonStrings.common_editing), + modifier = modifier, + onResetComposerMode = onResetComposerMode, + ) + } + is MessageComposerMode.EditCaption -> { + EditingModeView( + text = stringResource( + if (composerMode.content.isEmpty()) CommonStrings.common_adding_caption else CommonStrings.common_editing_caption + ), modifier = modifier, onResetComposerMode = onResetComposerMode, ) @@ -65,6 +75,7 @@ internal fun ComposerModeView( @Composable private fun EditingModeView( onResetComposerMode: () -> Unit, + text: String, modifier: Modifier = Modifier, ) { Row( @@ -76,14 +87,14 @@ private fun EditingModeView( ) { Icon( imageVector = CompoundIcons.Edit(), - contentDescription = stringResource(CommonStrings.common_editing), + contentDescription = null, tint = ElementTheme.materialColors.secondary, modifier = Modifier .padding(vertical = 8.dp) .size(16.dp), ) Text( - stringResource(CommonStrings.common_editing), + text = text, style = ElementTheme.typography.fontBodySmRegular, textAlign = TextAlign.Start, color = ElementTheme.materialColors.secondary, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index eca7340219..81cc7a739f 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -121,19 +121,25 @@ fun TextComposer( } val layoutModifier = modifier - .fillMaxSize() - .height(IntrinsicSize.Min) + .fillMaxSize() + .height(IntrinsicSize.Min) - val composerOptionsButton: @Composable () -> Unit = remember { + val composerOptionsButton: @Composable () -> Unit = remember(composerMode) { @Composable { - if (composerMode is MessageComposerMode.Attachment) { - Spacer(modifier = Modifier.width(9.dp)) - } else { - ComposerOptionsButton( - modifier = Modifier - .size(48.dp), - onClick = onAddAttachment - ) + when (composerMode) { + is MessageComposerMode.Attachment -> { + Spacer(modifier = Modifier.width(9.dp)) + } + is MessageComposerMode.EditCaption -> { + Spacer(modifier = Modifier.width(16.dp)) + } + else -> { + ComposerOptionsButton( + modifier = Modifier + .size(48.dp), + onClick = onAddAttachment + ) + } } } } @@ -331,8 +337,8 @@ private fun StandardLayout( if (voiceMessageState is VoiceMessageState.Preview || voiceMessageState is VoiceMessageState.Recording) { Box( modifier = Modifier - .padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp) - .size(48.dp), + .padding(bottom = 5.dp, top = 5.dp, end = 3.dp, start = 3.dp) + .size(48.dp), contentAlignment = Alignment.Center, ) { voiceDeleteButton() @@ -342,8 +348,8 @@ private fun StandardLayout( } Box( modifier = Modifier - .padding(bottom = 8.dp, top = 8.dp) - .weight(1f) + .padding(bottom = 8.dp, top = 8.dp) + .weight(1f) ) { voiceRecording() } @@ -356,16 +362,16 @@ private fun StandardLayout( } Box( modifier = Modifier - .padding(bottom = 8.dp, top = 8.dp) - .weight(1f) + .padding(bottom = 8.dp, top = 8.dp) + .weight(1f) ) { textInput() } } Box( - Modifier - .padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp) - .size(48.dp), + Modifier + .padding(bottom = 5.dp, top = 5.dp, end = 6.dp, start = 6.dp) + .size(48.dp), contentAlignment = Alignment.Center, ) { endButton() @@ -387,8 +393,8 @@ private fun TextFormattingLayout( ) { Box( modifier = Modifier - .weight(1f) - .padding(horizontal = 12.dp) + .weight(1f) + .padding(horizontal = 12.dp) ) { textInput() } @@ -432,11 +438,11 @@ private fun TextInputBox( Column( modifier = Modifier - .clip(roundedCorners) - .border(0.5.dp, borderColor, roundedCorners) - .background(color = bgColor) - .requiredHeightIn(min = 42.dp) - .fillMaxSize(), + .clip(roundedCorners) + .border(0.5.dp, borderColor, roundedCorners) + .background(color = bgColor) + .requiredHeightIn(min = 42.dp) + .fillMaxSize(), ) { if (composerMode is MessageComposerMode.Special) { ComposerModeView( @@ -447,9 +453,9 @@ private fun TextInputBox( val defaultTypography = ElementTheme.typography.fontBodyLgRegular Box( modifier = Modifier - .padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp) - // Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail - .then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier), + .padding(top = 4.dp, bottom = 4.dp, start = 12.dp, end = 12.dp) + // Apply test tag only once, otherwise 2 nodes will have it (both the normal and subcomposing one) and tests will fail + .then(if (!subcomposing) Modifier.testTag(TestTags.textEditor) else Modifier), contentAlignment = Alignment.CenterStart, ) { // Placeholder @@ -495,8 +501,8 @@ private fun TextInput( // This prevents it gaining focus and mutating the state. registerStateUpdates = !subcomposing, modifier = Modifier - .padding(top = 6.dp, bottom = 6.dp) - .fillMaxWidth(), + .padding(top = 6.dp, bottom = 6.dp) + .fillMaxWidth(), style = ElementRichTextEditorStyle.composerStyle(hasFocus = state.hasFocus), resolveMentionDisplay = resolveMentionDisplay, resolveRoomMentionDisplay = resolveRoomMentionDisplay, @@ -573,6 +579,40 @@ internal fun TextComposerEditPreview() = ElementPreview { } } +@PreviewsDayNight +@Composable +internal fun TextComposerEditCaptionPreview() = ElementPreview { + PreviewColumn( + items = aTextEditorStateRichList() + ) { _, textEditorState -> + ATextComposer( + state = textEditorState, + voiceMessageState = VoiceMessageState.Idle, + composerMode = aMessageComposerModeEditCaption( + content = "A caption", + ), + enableVoiceMessages = false, + ) + } +} + +@PreviewsDayNight +@Composable +internal fun TextComposerAddCaptionPreview() = ElementPreview { + PreviewColumn( + items = aTextEditorStateRichList() + ) { _, textEditorState -> + ATextComposer( + state = textEditorState, + voiceMessageState = VoiceMessageState.Idle, + composerMode = aMessageComposerModeEditCaption( + content = "", + ), + enableVoiceMessages = false, + ) + } +} + @PreviewsDayNight @Composable internal fun MarkdownTextComposerEditPreview() = ElementPreview { @@ -717,6 +757,14 @@ fun aMessageComposerModeEdit( content = content ) +fun aMessageComposerModeEditCaption( + eventOrTransactionId: EventOrTransactionId = EventId("$1234").toEventOrTransactionId(), + content: String = "Some caption", +) = MessageComposerMode.EditCaption( + eventOrTransactionId = eventOrTransactionId, + content = content +) + fun aMessageComposerModeReply( replyToDetails: InReplyToDetails, hideImage: Boolean = false, diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt index 6b5d601333..b7b0368aeb 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/SendButton.kt @@ -53,16 +53,16 @@ internal fun SendButton( onClick = onClick, enabled = canSendMessage, ) { - val iconVector = when (composerMode) { - is MessageComposerMode.Edit -> CompoundIcons.Check() + val iconVector = when { + composerMode.isEditing -> CompoundIcons.Check() else -> CompoundIcons.SendSolid() } - val iconStartPadding = when (composerMode) { - is MessageComposerMode.Edit -> 0.dp + val iconStartPadding = when { + composerMode.isEditing -> 0.dp else -> 2.dp } - val contentDescription = when (composerMode) { - is MessageComposerMode.Edit -> stringResource(CommonStrings.action_edit) + val contentDescription = when { + composerMode.isEditing -> stringResource(CommonStrings.action_edit) else -> stringResource(CommonStrings.action_send) } Box( diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt index 1915359c83..4ef6cfc019 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/MessageComposerMode.kt @@ -27,6 +27,11 @@ sealed interface MessageComposerMode { val content: String ) : Special + data class EditCaption( + val eventOrTransactionId: EventOrTransactionId, + val content: String + ) : Special + data class Reply( val replyToDetails: InReplyToDetails, val hideImage: Boolean, @@ -34,16 +39,8 @@ sealed interface MessageComposerMode { val eventId: EventId = replyToDetails.eventId() } - val relatedEventId: EventId? - get() = when (this) { - is Normal, - is Attachment -> null - is Edit -> eventOrTransactionId.eventId - is Reply -> eventId - } - val isEditing: Boolean - get() = this is Edit + get() = this is Edit || this is EditCaption val isReply: Boolean get() = this is Reply diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index ab3a93a692..2245657b6b 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -32,6 +32,7 @@ "Record voice message." "Stop recording" "Accept" + "Add caption" "Add to timeline" "Back" "Call" @@ -57,6 +58,7 @@ "Discard" "Done" "Edit" + "Edit caption" "Edit poll" "Enable" "End poll" @@ -91,6 +93,7 @@ "React" "Reject" "Remove" + "Remove caption" "Reply" "Reply in thread" "Report bug" @@ -123,6 +126,7 @@ "Yes" "About" "Acceptable use policy" + "Adding caption" "Advanced settings" "Analytics" "Appearance" @@ -143,6 +147,7 @@ "Do not show this again" "(edited)" "Editing" + "Editing caption" "* %1$s %2$s" "Encryption" "Encryption enabled"