Add support for adding/editing/removing media captions. (#3547)

* Add support for editing media captions.

* Add composer snapshots.

* PR comment.
This commit is contained in:
Doug
2024-11-21 18:18:27 +00:00
committed by GitHub
parent c081e538b4
commit 7e1476d973
21 changed files with 178 additions and 61 deletions
@@ -100,10 +100,7 @@ class TimelineInteractionHandler {
switch action {
case .copy:
guard let messageTimelineItem = timelineItem as? EventBasedMessageTimelineItemProtocol else {
return
}
guard let messageTimelineItem = timelineItem as? EventBasedMessageTimelineItemProtocol else { return }
UIPasteboard.general.string = messageTimelineItem.body
case .edit:
switch timelineItem {
@@ -118,6 +115,19 @@ class TimelineInteractionHandler {
default:
MXLog.error("Cannot edit item with id: \(timelineItem.id)")
}
case .addCaption, .editCaption:
switch timelineItem {
case let messageTimelineItem as EventBasedMessageTimelineItemProtocol:
processEditMessageEvent(messageTimelineItem)
default:
MXLog.error("Cannot add/edit caption on item with id: \(timelineItem.id)")
}
case .removeCaption:
guard case let .event(_, eventOrTransactionID) = timelineItem.id else {
MXLog.error("Failed removing caption, missing event ID")
return
}
Task { await timelineController.removeCaption(eventOrTransactionID) }
case .copyPermalink:
guard let eventID = eventTimelineItem.id.eventID else {
actionsSubject.send(.displayErrorToast(L10n.errorFailedCreatingThePermalink))
@@ -133,17 +143,10 @@ class TimelineInteractionHandler {
UIPasteboard.general.url = permalinkURL
}
case .redact:
guard case let .event(_, eventOrTransactionID) = itemID else {
fatalError()
}
Task {
await timelineController.redact(eventOrTransactionID)
}
guard case let .event(_, eventOrTransactionID) = itemID else { fatalError() }
Task { await timelineController.redact(eventOrTransactionID) }
case .reply:
guard let eventID = eventTimelineItem.id.eventID else {
return
}
guard let eventID = eventTimelineItem.id.eventID else { return }
let replyInfo = buildReplyInfo(for: eventTimelineItem)
let replyDetails = TimelineItemReplyDetails.loaded(sender: eventTimelineItem.sender, eventID: eventID, eventContent: replyInfo.type)
@@ -156,21 +159,14 @@ class TimelineInteractionHandler {
MXLog.info("Showing debug info for \(eventTimelineItem.id)")
actionsSubject.send(.showDebugInfo(debugInfo))
case .retryDecryption(let sessionID):
Task {
await timelineController.retryDecryption(for: sessionID)
}
Task { await timelineController.retryDecryption(for: sessionID) }
case .report:
actionsSubject.send(.displayReportContent(itemID: itemID, senderID: eventTimelineItem.sender.id))
case .react:
displayEmojiPicker(for: itemID)
case .toggleReaction(let key):
Task {
guard case let .event(_, eventOrTransactionID) = itemID else {
fatalError()
}
await timelineController.toggleReaction(key, to: eventOrTransactionID)
}
guard case let .event(_, eventOrTransactionID) = itemID else { fatalError() }
Task { await timelineController.toggleReaction(key, to: eventOrTransactionID) }
case .endPoll(let pollStartID):
endPoll(pollStartID: pollStartID)
case .pin:
@@ -202,18 +198,35 @@ class TimelineInteractionHandler {
let text: String
var htmlText: String?
var editType = ComposerMode.EditType.default
switch messageTimelineItem.contentType {
case .text(let content):
text = content.body
htmlText = content.formattedBodyHTMLString
case .emote(let content):
text = "/me " + content.body
case .audio(let content):
text = content.caption ?? ""
htmlText = content.formattedCaptionHTMLString
editType = text.isEmpty ? .addCaption : .editCaption
case .file(let content):
text = content.caption ?? ""
htmlText = content.formattedCaptionHTMLString
editType = text.isEmpty ? .addCaption : .editCaption
case .image(let content):
text = content.caption ?? ""
htmlText = content.formattedCaptionHTMLString
editType = text.isEmpty ? .addCaption : .editCaption
case .video(let content):
text = content.caption ?? ""
htmlText = content.formattedCaptionHTMLString
editType = text.isEmpty ? .addCaption : .editCaption
default:
text = messageTimelineItem.body
}
// Always update the mode first and then the text so that the composer has time to save the text draft
actionsSubject.send(.composer(action: .setMode(mode: .edit(originalEventOrTransactionID: eventOrTransactionID))))
actionsSubject.send(.composer(action: .setMode(mode: .edit(originalEventOrTransactionID: eventOrTransactionID, type: editType))))
actionsSubject.send(.composer(action: .setText(plainText: text, htmlText: htmlText)))
}
@@ -606,11 +606,17 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
html: html,
inReplyToEventID: eventID,
intentionalMentions: intentionalMentions)
case .edit(let originalEventOrTransactionID):
case .edit(let originalEventOrTransactionID, .default):
await timelineController.edit(originalEventOrTransactionID,
message: message,
html: html,
intentionalMentions: intentionalMentions)
case .edit(let originalEventOrTransactionID, .addCaption),
.edit(let originalEventOrTransactionID, .editCaption):
await timelineController.editCaption(originalEventOrTransactionID,
message: message,
html: html,
intentionalMentions: intentionalMentions)
case .default:
switch slashCommand(message: message) {
case .join:
@@ -57,6 +57,9 @@ struct TimelineItemMenuReaction: Hashable {
enum TimelineItemMenuAction: Identifiable, Hashable {
case copy
case edit
case addCaption
case editCaption
case removeCaption
case copyPermalink
case redact
case reply(isThread: Bool)
@@ -76,7 +79,7 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
/// Whether the item should cancel a reply/edit occurring in the composer.
var switchToDefaultComposer: Bool {
switch self {
case .reply, .edit:
case .reply, .edit, .addCaption, .editCaption:
return false
default:
return true
@@ -106,7 +109,7 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
/// Whether or not the action is destructive.
var isDestructive: Bool {
switch self {
case .redact, .report:
case .redact, .report, .removeCaption:
return true
default:
return false
@@ -130,6 +133,12 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
Label(L10n.actionCopy, icon: \.copy)
case .edit:
Label(L10n.actionEdit, icon: \.edit)
case .addCaption:
Label(L10n.actionAddCaption, icon: \.edit)
case .editCaption:
Label(L10n.actionEditCaption, icon: \.edit)
case .removeCaption:
Label(L10n.actionRemoveCaption, icon: \.delete)
case .copyPermalink:
Label(L10n.actionCopyLinkToMessage, icon: \.link)
case .reply(let isThread):
@@ -65,7 +65,15 @@ struct TimelineItemMenuActionProvider {
}
if item.isEditable {
actions.append(.edit)
if let messageItem = item as? EventBasedMessageTimelineItemProtocol, messageItem.supportsMediaCaption {
if messageItem.hasMediaCaption {
actions.append(contentsOf: [.editCaption, .removeCaption])
} else {
actions.append(.addCaption)
}
} else if !(item is VoiceMessageRoomTimelineItem) {
actions.append(.edit)
}
}
if canCurrentUserPin, let eventID = item.id.eventID {