mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Add timeline properties for edited and reactions.
Not yet visible, waiting for timeline API.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright 2022 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 SwiftUI
|
||||
|
||||
/// A horizontal `ScrollView` that will pad the start of it's content
|
||||
/// when the alignment is set to `.trailing`.
|
||||
struct AlignedScrollView<Content: View>: View {
|
||||
let alignment: HorizontalAlignment
|
||||
var showsIndicators = true
|
||||
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
@State private var scrollViewFrame: CGRect = .zero
|
||||
|
||||
var body: some View {
|
||||
ScrollView(.horizontal, showsIndicators: showsIndicators) {
|
||||
content()
|
||||
.frame(minWidth: scrollViewFrame.width,
|
||||
alignment: Alignment(horizontal: alignment, vertical: .center))
|
||||
}
|
||||
.background(ViewFrameReader(frame: $scrollViewFrame))
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,9 @@ struct ViewFrameReader: View {
|
||||
.preference(key: FramePreferenceKey.self,
|
||||
value: geometry.frame(in: .local))
|
||||
}
|
||||
.onPreferenceChange(FramePreferenceKey.self) {
|
||||
frame = $0
|
||||
.onPreferenceChange(FramePreferenceKey.self) { newValue in
|
||||
guard frame != newValue else { return }
|
||||
frame = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ enum RoomScreenViewAction {
|
||||
case itemDisappeared(id: String)
|
||||
case linkClicked(url: URL)
|
||||
case sendMessage
|
||||
case sendReaction(key: String, eventID: String)
|
||||
}
|
||||
|
||||
struct RoomScreenViewState: BindableState {
|
||||
|
||||
@@ -89,6 +89,9 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
|
||||
await timelineController.sendMessage(state.bindings.composerText)
|
||||
state.bindings.composerText = ""
|
||||
case .sendReaction(let key, _):
|
||||
#warning("Reaction implementation awaiting SDK support.")
|
||||
MXLog.warning("React with \(key) failed. Not implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ struct RoomScreen: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0.0) {
|
||||
TimelineView(context: context)
|
||||
TimelineView()
|
||||
.environmentObject(context)
|
||||
|
||||
MessageComposer(text: $context.composerText, disabled: context.viewState.sendButtonDisabled) {
|
||||
sendMessage()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
@@ -25,7 +27,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
@ScaledMetric private var minBubbleWidth = 44
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: timelineItem.isOutgoing ? .trailing : .leading, spacing: -5) {
|
||||
VStack(alignment: alignment, spacing: -5) {
|
||||
if !timelineItem.isOutgoing {
|
||||
header
|
||||
.zIndex(1)
|
||||
@@ -33,18 +35,18 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
if timelineItem.isOutgoing {
|
||||
HStack {
|
||||
Spacer()
|
||||
styledContent
|
||||
styledContentWithReactions
|
||||
}
|
||||
.padding(.trailing, 16)
|
||||
.padding(.leading, 51)
|
||||
} else {
|
||||
styledContent
|
||||
styledContentWithReactions
|
||||
.padding(.leading, 16)
|
||||
.padding(.trailing, 51)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private var header: some View {
|
||||
if timelineItem.shouldShowSenderDetails {
|
||||
@@ -62,6 +64,22 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var styledContentWithReactions: some View {
|
||||
// Figma has a spacing of -4 but it doesn't take into account
|
||||
// the centre aligned stroke width so we use -5 here
|
||||
VStack(alignment: alignment, spacing: -5) {
|
||||
styledContent
|
||||
|
||||
if !timelineItem.properties.reactions.isEmpty {
|
||||
TimelineReactionsView(reactions: timelineItem.properties.reactions,
|
||||
alignment: alignment) { key in
|
||||
context.send(viewAction: .sendReaction(key: key, eventID: timelineItem.id))
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var styledContent: some View {
|
||||
@@ -84,9 +102,11 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
content()
|
||||
.frame(minWidth: minBubbleWidth, alignment: .leading)
|
||||
|
||||
Text(timelineItem.timestamp)
|
||||
.foregroundColor(Color.element.tertiaryContent)
|
||||
.font(.element.caption2)
|
||||
if timelineItem.properties.isEdited {
|
||||
Text(ElementL10n.editedSuffix)
|
||||
.font(.element.caption2)
|
||||
.foregroundColor(.element.tertiaryContent)
|
||||
}
|
||||
}
|
||||
.padding(EdgeInsets(top: 8, leading: 8, bottom: 4, trailing: 8))
|
||||
.clipped()
|
||||
@@ -103,6 +123,10 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
let opacity = colorScheme == .light ? 0.06 : 0.15
|
||||
return timelineItem.isOutgoing ? .element.accent.opacity(opacity) : .element.system
|
||||
}
|
||||
|
||||
private var alignment: HorizontalAlignment {
|
||||
timelineItem.isOutgoing ? .trailing : .leading
|
||||
}
|
||||
}
|
||||
|
||||
struct TimelineItemBubbledStylerView_Previews: PreviewProvider {
|
||||
|
||||
@@ -18,13 +18,19 @@ import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct TimelineItemPlainStylerView<Content: View>: View {
|
||||
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
header
|
||||
content()
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
content()
|
||||
supplementaryViews
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +50,24 @@ struct TimelineItemPlainStylerView<Content: View>: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var supplementaryViews: some View {
|
||||
VStack {
|
||||
if timelineItem.properties.isEdited {
|
||||
Text(ElementL10n.editedSuffix)
|
||||
.font(.element.footnote)
|
||||
.foregroundColor(.element.tertiaryContent)
|
||||
}
|
||||
|
||||
if !timelineItem.properties.reactions.isEmpty {
|
||||
TimelineReactionsView(reactions: timelineItem.properties.reactions,
|
||||
alignment: .leading) { key in
|
||||
context.send(viewAction: .sendReaction(key: key, eventID: timelineItem.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TimelineItemPlainStylerView_Previews: PreviewProvider {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// Copyright 2022 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 SwiftUI
|
||||
|
||||
struct TimelineReactionsView: View {
|
||||
let reactions: [AggregatedReaction]
|
||||
let alignment: HorizontalAlignment
|
||||
let action: (String) -> Void
|
||||
|
||||
var body: some View {
|
||||
AlignedScrollView(alignment: alignment, showsIndicators: false) {
|
||||
HStack {
|
||||
ForEach(reactions, id: \.self) { reaction in
|
||||
TimelineReactionButton(reaction: reaction, action: action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TimelineReactionButton: View {
|
||||
let reaction: AggregatedReaction
|
||||
let action: (String) -> Void
|
||||
|
||||
var body: some View {
|
||||
Button { action(reaction.key) } label: { label }
|
||||
}
|
||||
|
||||
var label: some View {
|
||||
HStack(spacing: 4) {
|
||||
Text(reaction.key)
|
||||
.font(.element.caption1)
|
||||
Text(String(reaction.count))
|
||||
.font(.element.caption1)
|
||||
.foregroundColor(.element.secondaryContent)
|
||||
}
|
||||
.padding(.vertical, 5)
|
||||
.padding(.horizontal, 6)
|
||||
.background(
|
||||
Capsule()
|
||||
.strokeBorder(reaction.isHighlighted ? Color.element.accent : .element.background, lineWidth: 2)
|
||||
.background(reaction.isHighlighted ? Color.element.accent.opacity(0.1) : .element.system, in: Capsule())
|
||||
)
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
|
||||
struct TimelineReactionView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
TimelineReactionButton(reaction: AggregatedReaction(key: "👍", count: 5, isHighlighted: true)) { _ in }
|
||||
TimelineReactionButton(reaction: AggregatedReaction(key: "👏", count: 1, isHighlighted: false)) { _ in }
|
||||
TimelineReactionButton(reaction: AggregatedReaction(key: "🎉", count: 20, isHighlighted: false)) { _ in }
|
||||
|
||||
TimelineReactionsView(reactions: [
|
||||
AggregatedReaction(key: "😅", count: 1, isHighlighted: true),
|
||||
AggregatedReaction(key: "🤷♂️", count: 1, isHighlighted: false),
|
||||
AggregatedReaction(key: "🎨", count: 6, isHighlighted: true),
|
||||
AggregatedReaction(key: "🎉", count: 8, isHighlighted: false),
|
||||
AggregatedReaction(key: "🤯", count: 15, isHighlighted: true),
|
||||
AggregatedReaction(key: "🫣", count: 1, isHighlighted: false),
|
||||
AggregatedReaction(key: "🚀", count: 3, isHighlighted: true),
|
||||
AggregatedReaction(key: "😇", count: 2, isHighlighted: false),
|
||||
AggregatedReaction(key: "🤭", count: 9, isHighlighted: true),
|
||||
AggregatedReaction(key: "🫤", count: 10, isHighlighted: false)
|
||||
], alignment: .leading) { _ in }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,8 +27,7 @@ struct EmoteRoomTimelineView: View {
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
FormattedBodyText(text: timelineItem.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +60,7 @@ struct EmoteRoomTimelineView_Previews: PreviewProvider {
|
||||
timestamp: timestamp,
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: false,
|
||||
senderId: senderId)
|
||||
senderId: senderId,
|
||||
properties: RoomTimelineItemProperties())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ struct FormattedBodyText: View {
|
||||
}
|
||||
}
|
||||
|
||||
extension FormattedBodyText {
|
||||
init(text: String) {
|
||||
attributedComponents = [.init(attributedString: AttributedString(text), isBlockquote: false)]
|
||||
}
|
||||
}
|
||||
|
||||
struct FormattedBodyText_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
body.preferredColorScheme(.light)
|
||||
@@ -74,6 +80,7 @@ struct FormattedBodyText_Previews: PreviewProvider {
|
||||
]
|
||||
|
||||
let attributedStringBuilder = AttributedStringBuilder()
|
||||
|
||||
VStack(alignment: .leading, spacing: 24.0) {
|
||||
ForEach(htmlStrings, id: \.self) { htmlString in
|
||||
let attributedString = attributedStringBuilder.fromHTML(htmlString)
|
||||
@@ -83,6 +90,7 @@ struct FormattedBodyText_Previews: PreviewProvider {
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
FormattedBodyText(text: "Some plain text that's not an attributed component.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,8 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
||||
isOutgoing: false,
|
||||
senderId: "Bob",
|
||||
source: nil,
|
||||
image: UIImage(systemName: "photo")))
|
||||
image: UIImage(systemName: "photo"),
|
||||
properties: RoomTimelineItemProperties()))
|
||||
|
||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
||||
text: "Some other image",
|
||||
@@ -82,7 +83,8 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
||||
isOutgoing: false,
|
||||
senderId: "Bob",
|
||||
source: nil,
|
||||
image: nil))
|
||||
image: nil,
|
||||
properties: RoomTimelineItemProperties()))
|
||||
|
||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
||||
text: "Blurhashed image",
|
||||
@@ -93,7 +95,8 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
||||
source: nil,
|
||||
image: nil,
|
||||
aspectRatio: 0.7,
|
||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW",
|
||||
properties: RoomTimelineItemProperties()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ struct NoticeRoomTimelineView: View {
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
.foregroundColor(.element.primaryContent)
|
||||
FormattedBodyText(text: timelineItem.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,6 +61,7 @@ struct NoticeRoomTimelineView_Previews: PreviewProvider {
|
||||
timestamp: timestamp,
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: false,
|
||||
senderId: senderId)
|
||||
senderId: senderId,
|
||||
properties: RoomTimelineItemProperties())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,7 @@ struct TextRoomTimelineView: View {
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
.font(.body)
|
||||
.multilineTextAlignment(.leading)
|
||||
FormattedBodyText(text: timelineItem.text)
|
||||
}
|
||||
}
|
||||
.id(timelineItem.id)
|
||||
@@ -78,6 +76,7 @@ struct TextRoomTimelineView_Previews: PreviewProvider {
|
||||
timestamp: timestamp,
|
||||
shouldShowSenderDetails: shouldShowSenderDetails,
|
||||
isOutgoing: isOutgoing,
|
||||
senderId: senderId)
|
||||
senderId: senderId,
|
||||
properties: RoomTimelineItemProperties())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ struct TimelineItemList: View {
|
||||
@State private var hasPendingChanges = false
|
||||
@ObservedObject private var settings = ElementSettings.shared
|
||||
|
||||
@ObservedObject var context: RoomScreenViewModel.Context
|
||||
@EnvironmentObject var context: RoomScreenViewModel.Context
|
||||
|
||||
let bottomVisiblePublisher: PassthroughSubject<Bool, Never>
|
||||
let scrollToBottomPublisher: PassthroughSubject<Void, Never>
|
||||
@@ -163,6 +163,7 @@ struct TimelineItemList_Previews: PreviewProvider {
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
roomName: nil)
|
||||
|
||||
TimelineItemList(context: viewModel.context, bottomVisiblePublisher: PassthroughSubject(), scrollToBottomPublisher: PassthroughSubject())
|
||||
TimelineItemList(bottomVisiblePublisher: PassthroughSubject(), scrollToBottomPublisher: PassthroughSubject())
|
||||
.environmentObject(viewModel.context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,9 @@ struct TimelineView: View {
|
||||
@State private var scrollToBottomPublisher = PassthroughSubject<Void, Never>()
|
||||
@State private var scollToBottomButtonVisible = false
|
||||
|
||||
@ObservedObject var context: RoomScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .bottomTrailing) {
|
||||
TimelineItemList(context: context, bottomVisiblePublisher: bottomVisiblePublisher, scrollToBottomPublisher: scrollToBottomPublisher)
|
||||
TimelineItemList(bottomVisiblePublisher: bottomVisiblePublisher, scrollToBottomPublisher: scrollToBottomPublisher)
|
||||
scrollToBottomButton
|
||||
}
|
||||
}
|
||||
@@ -63,6 +61,7 @@ struct TimelineView_Previews: PreviewProvider {
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
roomName: nil)
|
||||
|
||||
TimelineView(context: viewModel.context)
|
||||
TimelineView()
|
||||
.environmentObject(viewModel.context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,14 +30,18 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
shouldShowSenderDetails: true,
|
||||
isOutgoing: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Some user with a really long long long long long display name"),
|
||||
senderDisplayName: "Some user with a really long long long long long display name",
|
||||
properties: RoomTimelineItemProperties(isEdited: true)),
|
||||
TextRoomTimelineItem(id: UUID().uuidString,
|
||||
text: "You also rule!",
|
||||
timestamp: "10:11 AM",
|
||||
shouldShowSenderDetails: false,
|
||||
isOutgoing: false,
|
||||
senderId: "",
|
||||
senderDisplayName: "Alice"),
|
||||
senderDisplayName: "Alice",
|
||||
properties: RoomTimelineItemProperties(reactions: [
|
||||
AggregatedReaction(key: "🙌", count: 1, isHighlighted: true)
|
||||
])),
|
||||
SeparatorRoomTimelineItem(id: UUID().uuidString,
|
||||
text: "Today"),
|
||||
TextRoomTimelineItem(id: UUID().uuidString,
|
||||
@@ -46,7 +50,11 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
shouldShowSenderDetails: false,
|
||||
isOutgoing: true,
|
||||
senderId: "",
|
||||
senderDisplayName: "Bob")]
|
||||
senderDisplayName: "Bob",
|
||||
properties: RoomTimelineItemProperties(reactions: [
|
||||
AggregatedReaction(key: "🙏", count: 1, isHighlighted: false),
|
||||
AggregatedReaction(key: "😁", count: 3, isHighlighted: false)
|
||||
]))]
|
||||
|
||||
func paginateBackwards(_ count: UInt) async -> Result<Void, RoomTimelineControllerError> {
|
||||
.failure(.generic)
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2022 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
|
||||
|
||||
/// Represents all reactions of the same type for a single event.
|
||||
struct AggregatedReaction: Equatable, Hashable {
|
||||
/// The reaction that was sent.
|
||||
let key: String
|
||||
/// The number of times this reactions was sent.
|
||||
let count: Int
|
||||
/// Whether to highlight the reaction, indicating that the current user sent this reaction.
|
||||
let isHighlighted: Bool
|
||||
}
|
||||
@@ -26,4 +26,6 @@ protocol EventBasedTimelineItemProtocol: RoomTimelineItemProtocol {
|
||||
var senderId: String { get }
|
||||
var senderDisplayName: String? { get set }
|
||||
var senderAvatar: UIImage? { get set }
|
||||
|
||||
var properties: RoomTimelineItemProperties { get }
|
||||
}
|
||||
|
||||
@@ -28,4 +28,6 @@ struct EmoteRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equa
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
var senderAvatar: UIImage?
|
||||
|
||||
let properties: RoomTimelineItemProperties
|
||||
}
|
||||
|
||||
@@ -35,4 +35,6 @@ struct ImageRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equa
|
||||
var height: CGFloat?
|
||||
var aspectRatio: CGFloat?
|
||||
var blurhash: String?
|
||||
|
||||
let properties: RoomTimelineItemProperties
|
||||
}
|
||||
|
||||
@@ -28,4 +28,6 @@ struct NoticeRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equ
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
var senderAvatar: UIImage?
|
||||
|
||||
let properties: RoomTimelineItemProperties
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// Copyright 2022 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
|
||||
|
||||
/// Properties of a matrix event that are common between all timeline items.
|
||||
struct RoomTimelineItemProperties: Equatable {
|
||||
/// Whether the item has been edited.
|
||||
var isEdited = false
|
||||
/// The aggregated reactions that have been sent for this item.
|
||||
var reactions: [AggregatedReaction] = []
|
||||
}
|
||||
@@ -28,4 +28,6 @@ struct TextRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equat
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
var senderAvatar: UIImage?
|
||||
|
||||
let properties: RoomTimelineItemProperties
|
||||
}
|
||||
|
||||
@@ -67,7 +67,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
isOutgoing: isOutgoing,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage)
|
||||
senderAvatar: avatarImage,
|
||||
properties: RoomTimelineItemProperties())
|
||||
}
|
||||
|
||||
private func buildImageTimelineItemFromMessage(_ message: ImageRoomMessage,
|
||||
@@ -94,7 +95,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
width: message.width,
|
||||
height: message.height,
|
||||
aspectRatio: aspectRatio,
|
||||
blurhash: message.blurhash)
|
||||
blurhash: message.blurhash,
|
||||
properties: RoomTimelineItemProperties())
|
||||
}
|
||||
|
||||
private func buildNoticeTimelineItemFromMessage(_ message: NoticeRoomMessage,
|
||||
@@ -113,7 +115,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
isOutgoing: isOutgoing,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage)
|
||||
senderAvatar: avatarImage,
|
||||
properties: RoomTimelineItemProperties())
|
||||
}
|
||||
|
||||
private func buildEmoteTimelineItemFromMessage(_ message: EmoteRoomMessage,
|
||||
@@ -132,6 +135,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
isOutgoing: isOutgoing,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage)
|
||||
senderAvatar: avatarImage,
|
||||
properties: RoomTimelineItemProperties())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user