Reply rendering (#861)

* Cleanup how we use message timeline items in preparation for rich reply rendering

* Expose reply details up to the UI level

* Render replies in the timeline (textual for now). Remove now unused AttributedStringBuilderComponent isReply flag.

* Move reply detail loading to when the item appears instead of when it's created.

* Fix unit tests

* Fetch event details also when the reply details are in the error state

* Tweaks following code review

* Localise emote bodies

* Fix replies not loading sometimes, use the current event identifier instead of the one being replied to
This commit is contained in:
Stefan Ceriu
2023-05-09 16:09:38 +03:00
committed by GitHub
parent 483d261504
commit 1bd6e461ed
48 changed files with 889 additions and 733 deletions
@@ -15,19 +15,21 @@
//
import Foundation
import UniformTypeIdentifiers
struct AudioRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
struct AudioRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
let body: String
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
let sender: TimelineItemSender
let duration: UInt64
let source: MediaSourceProxy?
var contentType: UTType?
let content: AudioRoomTimelineItemContent
var replyDetails: TimelineItemReplyDetails?
var properties = RoomTimelineItemProperties()
var body: String {
content.body
}
}
@@ -0,0 +1,25 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
import UniformTypeIdentifiers
struct AudioRoomTimelineItemContent: Hashable {
let body: String
let duration: UInt64
let source: MediaSourceProxy?
let contentType: UTType?
}
@@ -16,15 +16,21 @@
import UIKit
struct EmoteRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
struct EmoteRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
let body: String
var formattedBody: AttributedString?
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
let sender: TimelineItemSender
let content: EmoteRoomTimelineItemContent
var replyDetails: TimelineItemReplyDetails?
var properties = RoomTimelineItemProperties()
var body: String {
content.body
}
}
@@ -0,0 +1,22 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
struct EmoteRoomTimelineItemContent: Hashable {
let body: String
var formattedBody: AttributedString?
}
@@ -17,18 +17,21 @@
import UIKit
import UniformTypeIdentifiers
struct FileRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
struct FileRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
let body: String
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
let sender: TimelineItemSender
let source: MediaSourceProxy?
let thumbnailSource: MediaSourceProxy?
var contentType: UTType?
let content: FileRoomTimelineItemContent
var replyDetails: TimelineItemReplyDetails?
var properties = RoomTimelineItemProperties()
var body: String {
content.body
}
}
@@ -0,0 +1,25 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UniformTypeIdentifiers
struct FileRoomTimelineItemContent: Hashable {
let body: String
let source: MediaSourceProxy?
let thumbnailSource: MediaSourceProxy?
let contentType: UTType?
}
@@ -17,22 +17,21 @@
import UIKit
import UniformTypeIdentifiers
struct ImageRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
struct ImageRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
let body: String
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
let sender: TimelineItemSender
let source: MediaSourceProxy
let content: ImageRoomTimelineItemContent
var width: CGFloat?
var height: CGFloat?
var aspectRatio: CGFloat?
var blurhash: String?
var contentType: UTType?
var replyDetails: TimelineItemReplyDetails?
var properties = RoomTimelineItemProperties()
var body: String {
content.body
}
}
@@ -0,0 +1,29 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UniformTypeIdentifiers
struct ImageRoomTimelineItemContent: Hashable {
let body: String
let source: MediaSourceProxy
let thumbnailSource: MediaSourceProxy?
var width: CGFloat?
var height: CGFloat?
var aspectRatio: CGFloat?
var blurhash: String?
var contentType: UTType?
}
@@ -16,15 +16,21 @@
import UIKit
struct NoticeRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
struct NoticeRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
let body: String
var formattedBody: AttributedString?
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
let sender: TimelineItemSender
let content: NoticeRoomTimelineItemContent
var replyDetails: TimelineItemReplyDetails?
var properties = RoomTimelineItemProperties()
var body: String {
content.body
}
}
@@ -0,0 +1,22 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
struct NoticeRoomTimelineItemContent: Hashable {
let body: String
var formattedBody: AttributedString?
}
@@ -16,15 +16,22 @@
import UIKit
struct TextRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
struct TextRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
let body: String
var formattedBody: AttributedString?
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
let sender: TimelineItemSender
let content: TextRoomTimelineItemContent
var replyDetails: TimelineItemReplyDetails?
var properties = RoomTimelineItemProperties()
var body: String {
content.body
}
}
@@ -0,0 +1,22 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
struct TextRoomTimelineItemContent: Hashable {
let body: String
var formattedBody: AttributedString?
}
@@ -17,24 +17,21 @@
import UIKit
import UniformTypeIdentifiers
struct VideoRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
struct VideoRoomTimelineItem: EventBasedMessageTimelineItemProtocol, Identifiable, Hashable {
let id: String
let body: String
let timestamp: String
let isOutgoing: Bool
let isEditable: Bool
let sender: TimelineItemSender
let duration: UInt64
let source: MediaSourceProxy?
let thumbnailSource: MediaSourceProxy?
let content: VideoRoomTimelineItemContent
var width: CGFloat?
var height: CGFloat?
var aspectRatio: CGFloat?
var blurhash: String?
var contentType: UTType?
var replyDetails: TimelineItemReplyDetails?
var properties = RoomTimelineItemProperties()
var body: String {
content.body
}
}
@@ -0,0 +1,30 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UniformTypeIdentifiers
struct VideoRoomTimelineItemContent: Hashable {
let body: String
let duration: UInt64
let source: MediaSourceProxy?
let thumbnailSource: MediaSourceProxy?
var width: CGFloat?
var height: CGFloat?
var aspectRatio: CGFloat?
var blurhash: String?
var contentType: UTType?
}