mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Message editing (#298)
* Add translation for editing mode * Add `is_editable` flag to timeline item * Add edit api implementation * Add edit context menu action * Add changelog * Update Rust package * Add isEditable into video timeline item * Fix cyclomatic complexity * Fix video item thumbnail loading * Fix not updating timeline layout
This commit is contained in:
@@ -65,6 +65,10 @@ struct MockRoomProxy: RoomProxyProtocol {
|
||||
func sendMessage(_ message: String, inReplyToEventId: String? = nil) async -> Result<Void, RoomProxyError> {
|
||||
.failure(.failedSendingMessage)
|
||||
}
|
||||
|
||||
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> {
|
||||
.failure(.failedSendingMessage)
|
||||
}
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomProxyError> {
|
||||
.failure(.failedRedactingEvent)
|
||||
|
||||
@@ -188,6 +188,24 @@ class RoomProxy: RoomProxyProtocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError> {
|
||||
sendMessageBgTask = backgroundTaskService.startBackgroundTask(withName: "SendMessage", isReusable: true)
|
||||
defer {
|
||||
sendMessageBgTask?.stop()
|
||||
}
|
||||
|
||||
let transactionId = genTransactionId()
|
||||
|
||||
return await Task.dispatch(on: .global()) {
|
||||
do {
|
||||
try self.room.edit(newMsg: newMessage, originalEventId: originalEventId, txnId: transactionId)
|
||||
return .success(())
|
||||
} catch {
|
||||
return .failure(.failedSendingMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomProxyError> {
|
||||
let transactionID = genTransactionId()
|
||||
|
||||
@@ -59,6 +59,8 @@ protocol RoomProxyProtocol {
|
||||
func paginateBackwards(count: UInt) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func sendMessage(_ message: String, inReplyToEventId: String?) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func editMessage(_ newMessage: String, originalEventId: String) async -> Result<Void, RoomProxyError>
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomProxyError>
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timestamp: "10:10 AM",
|
||||
inGroupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Jacob",
|
||||
properties: RoomTimelineItemProperties(isEdited: true)),
|
||||
@@ -38,6 +39,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timestamp: "10:11 AM",
|
||||
inGroupState: .beginning,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Helena",
|
||||
properties: RoomTimelineItemProperties(reactions: [
|
||||
@@ -48,6 +50,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timestamp: "10:11 AM",
|
||||
inGroupState: .end,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Helena",
|
||||
properties: RoomTimelineItemProperties(reactions: [
|
||||
@@ -61,6 +64,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timestamp: "5 PM",
|
||||
inGroupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Helena"),
|
||||
TextRoomTimelineItem(id: UUID().uuidString,
|
||||
@@ -68,6 +72,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timestamp: "5 PM",
|
||||
inGroupState: .beginning,
|
||||
isOutgoing: true,
|
||||
isEditable: true,
|
||||
senderId: "",
|
||||
senderDisplayName: "Bob"),
|
||||
TextRoomTimelineItem(id: UUID().uuidString,
|
||||
@@ -75,6 +80,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timestamp: "5 PM",
|
||||
inGroupState: .end,
|
||||
isOutgoing: true,
|
||||
isEditable: true,
|
||||
senderId: "",
|
||||
senderDisplayName: "Bob",
|
||||
properties: RoomTimelineItemProperties(reactions: [
|
||||
@@ -91,6 +97,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timestamp: "5 PM",
|
||||
inGroupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Helena")
|
||||
]
|
||||
@@ -106,6 +113,8 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
func sendMessage(_ message: String) async { }
|
||||
|
||||
func sendReply(_ message: String, to itemId: String) async { }
|
||||
|
||||
func editMessage(_ newMessage: String, of itemId: String) async { }
|
||||
|
||||
func redact(_ eventID: String) async { }
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ struct MockRoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
func sendMessage(_ message: String, inReplyToItemId: String?) async -> Result<Void, RoomTimelineProviderError> {
|
||||
.failure(.failedSendingMessage)
|
||||
}
|
||||
|
||||
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> {
|
||||
.failure(.failedSendingMessage)
|
||||
}
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomTimelineProviderError> {
|
||||
.failure(.failedRedactingItem)
|
||||
|
||||
@@ -121,6 +121,13 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func editMessage(_ newMessage: String, of itemId: String) async {
|
||||
switch await timelineProvider.editMessage(newMessage, originalItemId: itemId) {
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func redact(_ eventID: String) async {
|
||||
switch await timelineProvider.redact(eventID) {
|
||||
@@ -273,7 +280,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
switch await mediaProvider.loadImageFromSource(source) {
|
||||
case .success(let image):
|
||||
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
|
||||
var item = timelineItems[index] as? ImageRoomTimelineItem else {
|
||||
var item = timelineItems[index] as? VideoRoomTimelineItem else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ protocol RoomTimelineControllerProtocol {
|
||||
|
||||
func sendReply(_ message: String, to itemId: String) async
|
||||
|
||||
func editMessage(_ newMessage: String, of itemId: String) async
|
||||
|
||||
func redact(_ eventID: String) async
|
||||
|
||||
func debugDescriptionFor(_ itemId: String) -> String
|
||||
|
||||
@@ -84,6 +84,15 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol {
|
||||
return .failure(.failedSendingMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError> {
|
||||
switch await roomProxy.editMessage(newMessage, originalEventId: originalItemId) {
|
||||
case .success:
|
||||
return .success(())
|
||||
case .failure:
|
||||
return .failure(.failedSendingMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomTimelineProviderError> {
|
||||
switch await roomProxy.redact(eventID) {
|
||||
|
||||
@@ -33,6 +33,8 @@ protocol RoomTimelineProviderProtocol {
|
||||
func paginateBackwards(_ count: UInt) async -> Result<Void, RoomTimelineProviderError>
|
||||
|
||||
func sendMessage(_ message: String, inReplyToItemId: String?) async -> Result<Void, RoomTimelineProviderError>
|
||||
|
||||
func editMessage(_ newMessage: String, originalItemId: String) async -> Result<Void, RoomTimelineProviderError>
|
||||
|
||||
func redact(_ eventID: String) async -> Result<Void, RoomTimelineProviderError>
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ struct MessageTimelineItem<Content: MessageContentProtocol> {
|
||||
var isEdited: Bool {
|
||||
item.content().asMessage()?.isEdited() == true
|
||||
}
|
||||
|
||||
var isEditable: Bool {
|
||||
item.isEditable()
|
||||
}
|
||||
|
||||
var inReplyTo: String? {
|
||||
item.content().asMessage()?.inReplyTo()
|
||||
|
||||
@@ -78,6 +78,10 @@ struct EventTimelineItemProxy: CustomDebugStringConvertible {
|
||||
var isOwn: Bool {
|
||||
item.isOwn()
|
||||
}
|
||||
|
||||
var isEditable: Bool {
|
||||
item.isEditable()
|
||||
}
|
||||
|
||||
var sender: String {
|
||||
item.sender()
|
||||
|
||||
@@ -52,6 +52,7 @@ protocol EventBasedTimelineItemProtocol: RoomTimelineItemProtocol {
|
||||
var shouldShowSenderDetails: Bool { get }
|
||||
var inGroupState: TimelineItemInGroupState { get }
|
||||
var isOutgoing: Bool { get }
|
||||
var isEditable: Bool { get }
|
||||
|
||||
var senderId: String { get }
|
||||
var senderDisplayName: String? { get set }
|
||||
|
||||
@@ -24,6 +24,7 @@ struct EmoteRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equa
|
||||
let timestamp: String
|
||||
let inGroupState: TimelineItemInGroupState
|
||||
let isOutgoing: Bool
|
||||
let isEditable: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
|
||||
@@ -29,6 +29,7 @@ struct EncryptedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable,
|
||||
let timestamp: String
|
||||
let inGroupState: TimelineItemInGroupState
|
||||
let isOutgoing: Bool
|
||||
let isEditable: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
|
||||
@@ -23,6 +23,7 @@ struct ImageRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equa
|
||||
let timestamp: String
|
||||
let inGroupState: TimelineItemInGroupState
|
||||
let isOutgoing: Bool
|
||||
let isEditable: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
|
||||
@@ -24,6 +24,7 @@ struct NoticeRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equ
|
||||
let timestamp: String
|
||||
let inGroupState: TimelineItemInGroupState
|
||||
let isOutgoing: Bool
|
||||
let isEditable: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
|
||||
@@ -23,6 +23,7 @@ struct RedactedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, E
|
||||
let timestamp: String
|
||||
let inGroupState: TimelineItemInGroupState
|
||||
let isOutgoing: Bool
|
||||
let isEditable: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
|
||||
@@ -24,6 +24,7 @@ struct TextRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equat
|
||||
let timestamp: String
|
||||
let inGroupState: TimelineItemInGroupState
|
||||
let isOutgoing: Bool
|
||||
let isEditable: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
|
||||
@@ -23,6 +23,7 @@ struct VideoRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equa
|
||||
let timestamp: String
|
||||
let inGroupState: TimelineItemInGroupState
|
||||
let isOutgoing: Bool
|
||||
let isEditable: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
|
||||
@@ -98,6 +98,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
timestamp: eventItemProxy.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
inGroupState: inGroupState,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: eventItemProxy.isEditable,
|
||||
senderId: eventItemProxy.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
@@ -114,6 +115,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
timestamp: eventItemProxy.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
inGroupState: inGroupState,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: eventItemProxy.isEditable,
|
||||
senderId: eventItemProxy.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
@@ -134,6 +136,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
timestamp: eventItemProxy.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
inGroupState: inGroupState,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: eventItemProxy.isEditable,
|
||||
senderId: eventItemProxy.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
@@ -155,6 +158,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
inGroupState: inGroupState,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: message.isEditable,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
@@ -178,6 +182,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
inGroupState: inGroupState,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: message.isEditable,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
@@ -207,6 +212,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
inGroupState: inGroupState,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: message.isEditable,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
@@ -236,6 +242,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
inGroupState: inGroupState,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: message.isEditable,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
@@ -257,6 +264,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
inGroupState: inGroupState,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: message.isEditable,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
|
||||
Reference in New Issue
Block a user