mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Made room state event collapsing configurable, expose it in the developer menu. Allow the UserSetting property wrappers to only store values in memory (+11 squashed commits)
Squashed commits: [42e45fc] Even more tweaks following code review [5dcd5be] Add swift-algoritms and switch bubbling detection to its `chunked` method [4ac70ed] Move the groupBy implementation to Collection instead of Array [6aeffc3] Tweaks following code review [0ca5ac2] Bubbles working again, grouping computed closer to the UI level [3a66030] Refactor how timeline items are built in the RoomTimelineController [57d51e9] Remove grouping from timeline items [8608950] Remove the RoomTimelineViewFactory, update the GroupRoomTimelineView [9e52e61] Add array grouping extension, unit tests and switched the timeline controller to it [7d213a1] First attempt [90bb1d7] Remove now unused `RoomTimelineController` `updatedTimelineItem` calback
This commit is contained in:
committed by
Stefan Ceriu
parent
64902190d4
commit
a3c7c47a69
@@ -22,6 +22,10 @@ struct DeveloperOptionsScreenViewState: BindableState {
|
||||
var bindings: DeveloperOptionsScreenViewStateBindings
|
||||
}
|
||||
|
||||
struct DeveloperOptionsScreenViewStateBindings { }
|
||||
struct DeveloperOptionsScreenViewStateBindings {
|
||||
var shouldCollapseRoomStateEvents: Bool
|
||||
}
|
||||
|
||||
enum DeveloperOptionsScreenViewAction { }
|
||||
enum DeveloperOptionsScreenViewAction {
|
||||
case changedShouldCollapseRoomStateEvents
|
||||
}
|
||||
|
||||
+13
-2
@@ -20,8 +20,19 @@ typealias DeveloperOptionsScreenViewModelType = StateStoreViewModel<DeveloperOpt
|
||||
|
||||
class DeveloperOptionsScreenViewModel: DeveloperOptionsScreenViewModelType, DeveloperOptionsScreenViewModelProtocol {
|
||||
var callback: ((DeveloperOptionsScreenViewModelAction) -> Void)?
|
||||
|
||||
|
||||
init() {
|
||||
super.init(initialViewState: DeveloperOptionsScreenViewState(bindings: DeveloperOptionsScreenViewStateBindings()))
|
||||
super.init(initialViewState: DeveloperOptionsScreenViewState(bindings: DeveloperOptionsScreenViewStateBindings(shouldCollapseRoomStateEvents: ServiceLocator.shared.settings.shouldCollapseRoomStateEvents)))
|
||||
|
||||
ServiceLocator.shared.settings.$shouldCollapseRoomStateEvents
|
||||
.weakAssign(to: \.state.bindings.shouldCollapseRoomStateEvents, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
override func process(viewAction: DeveloperOptionsScreenViewAction) async {
|
||||
switch viewAction {
|
||||
case .changedShouldCollapseRoomStateEvents:
|
||||
ServiceLocator.shared.settings.shouldCollapseRoomStateEvents = state.bindings.shouldCollapseRoomStateEvents
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -22,6 +22,15 @@ struct DeveloperOptionsScreenScreen: View {
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
Toggle(isOn: $context.shouldCollapseRoomStateEvents) {
|
||||
Text("Collapse room state events")
|
||||
}
|
||||
.onChange(of: context.shouldCollapseRoomStateEvents) { _ in
|
||||
context.send(viewAction: .changedShouldCollapseRoomStateEvents)
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
Button {
|
||||
showConfetti = true
|
||||
|
||||
@@ -36,7 +36,6 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = RoomScreenViewModel(timelineController: parameters.timelineController,
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
mediaProvider: parameters.mediaProvider,
|
||||
roomName: parameters.roomProxy.displayName ?? parameters.roomProxy.name,
|
||||
roomAvatarUrl: parameters.roomProxy.avatarURL)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Algorithms
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
@@ -27,15 +28,12 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
|
||||
private let timelineController: RoomTimelineControllerProtocol
|
||||
private let timelineViewFactory: RoomTimelineViewFactoryProtocol
|
||||
|
||||
init(timelineController: RoomTimelineControllerProtocol,
|
||||
timelineViewFactory: RoomTimelineViewFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
roomName: String?,
|
||||
roomAvatarUrl: URL? = nil) {
|
||||
self.timelineController = timelineController
|
||||
self.timelineViewFactory = timelineViewFactory
|
||||
|
||||
super.init(initialViewState: RoomScreenViewState(roomId: timelineController.roomID,
|
||||
roomTitle: roomName ?? "Unknown room 💥",
|
||||
@@ -52,13 +50,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
switch callback {
|
||||
case .updatedTimelineItems:
|
||||
self.buildTimelineViews()
|
||||
case .updatedTimelineItem(let itemId):
|
||||
guard let timelineItem = self.timelineController.timelineItems.first(where: { $0.id == itemId }),
|
||||
let viewIndex = self.state.items.firstIndex(where: { $0.id == itemId }) else {
|
||||
return
|
||||
}
|
||||
|
||||
self.state.items[viewIndex] = timelineViewFactory.buildTimelineViewFor(timelineItem: timelineItem)
|
||||
case .canBackPaginate(let canBackPaginate):
|
||||
if self.state.canBackPaginate != canBackPaginate {
|
||||
self.state.canBackPaginate = canBackPaginate
|
||||
@@ -161,13 +152,57 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
|
||||
private func buildTimelineViews() {
|
||||
let stateItems = timelineController.timelineItems.map { item in
|
||||
timelineViewFactory.buildTimelineViewFor(timelineItem: item)
|
||||
var timelineViews = [RoomTimelineViewProvider]()
|
||||
|
||||
let itemsGroupedByTimelineDisplayStyle = timelineController.timelineItems.chunked { current, next in
|
||||
canGroupItem(timelineItem: current, with: next)
|
||||
}
|
||||
|
||||
state.items = stateItems
|
||||
for itemGroup in itemsGroupedByTimelineDisplayStyle {
|
||||
guard !itemGroup.isEmpty else {
|
||||
MXLog.error("Found empty item group")
|
||||
continue
|
||||
}
|
||||
|
||||
if itemGroup.count == 1 {
|
||||
if let firstItem = itemGroup.first {
|
||||
timelineViews.append(RoomTimelineViewProvider(timelineItem: firstItem, grouping: .single))
|
||||
}
|
||||
} else {
|
||||
for (index, item) in itemGroup.enumerated() {
|
||||
if index == 0 {
|
||||
timelineViews.append(RoomTimelineViewProvider(timelineItem: item, grouping: .first))
|
||||
} else if index == itemGroup.count - 1 {
|
||||
timelineViews.append(RoomTimelineViewProvider(timelineItem: item, grouping: .last))
|
||||
} else {
|
||||
timelineViews.append(RoomTimelineViewProvider(timelineItem: item, grouping: .middle))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.items = timelineViews
|
||||
}
|
||||
|
||||
|
||||
private func canGroupItem(timelineItem: RoomTimelineItemProtocol, with otherTimelineItem: RoomTimelineItemProtocol) -> Bool {
|
||||
if timelineItem is CollapsibleTimelineItem || otherTimelineItem is CollapsibleTimelineItem {
|
||||
return false
|
||||
}
|
||||
|
||||
guard let eventTimelineItem = timelineItem as? EventBasedTimelineItemProtocol,
|
||||
let otherEventTimelineItem = otherTimelineItem as? EventBasedTimelineItemProtocol else {
|
||||
return false
|
||||
}
|
||||
|
||||
// State events aren't rendered as messages so shouldn't be grouped.
|
||||
if eventTimelineItem is StateRoomTimelineItem || otherEventTimelineItem is StateRoomTimelineItem {
|
||||
return false
|
||||
}
|
||||
|
||||
// can be improved by adding a date threshold
|
||||
return otherEventTimelineItem.properties.reactions.isEmpty && eventTimelineItem.sender == otherEventTimelineItem.sender
|
||||
}
|
||||
|
||||
private func sendCurrentMessage() async {
|
||||
guard !state.bindings.composerText.isEmpty else {
|
||||
fatalError("This message should never be empty")
|
||||
@@ -298,7 +333,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
|
||||
extension RoomScreenViewModel {
|
||||
static let mock = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
mediaProvider: MockMediaProvider(),
|
||||
roomName: "Preview room")
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ struct RoomHeaderView_Previews: PreviewProvider {
|
||||
@ViewBuilder
|
||||
static var bodyPlain: some View {
|
||||
let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
mediaProvider: MockMediaProvider(),
|
||||
roomName: "Some Room name",
|
||||
roomAvatarUrl: URL.picturesDirectory)
|
||||
@@ -67,11 +66,10 @@ struct RoomHeaderView_Previews: PreviewProvider {
|
||||
.previewLayout(.sizeThatFits)
|
||||
.padding()
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
static var bodyEncrypted: some View {
|
||||
let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
mediaProvider: MockMediaProvider(),
|
||||
roomName: "Some Room name",
|
||||
roomAvatarUrl: nil)
|
||||
|
||||
@@ -125,7 +125,6 @@ struct RoomScreen: View {
|
||||
struct RoomScreen_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
mediaProvider: MockMediaProvider(),
|
||||
roomName: "Preview room")
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ import SwiftUI
|
||||
|
||||
struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||
@Environment(\.timelineGroupStyle) private var timelineStyleGrouping
|
||||
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
@ViewBuilder let content: () -> Content
|
||||
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@ScaledMetric private var senderNameVerticalPadding = 3
|
||||
private let cornerRadius: CGFloat = 12
|
||||
|
||||
@@ -54,7 +54,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
|
||||
@ViewBuilder
|
||||
private var header: some View {
|
||||
if timelineItem.shouldShowSenderDetails {
|
||||
if shouldShowSenderDetails {
|
||||
VStack {
|
||||
Spacer()
|
||||
.frame(height: 8)
|
||||
@@ -90,7 +90,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
|
||||
var messageBubble: some View {
|
||||
styledContent
|
||||
.contentShape(.contextMenuPreview, RoundedCornerShape(radius: cornerRadius, corners: timelineItem.roundedCorners)) // Rounded corners for the context menu animation.
|
||||
.contentShape(.contextMenuPreview, RoundedCornerShape(radius: cornerRadius, corners: roundedCorners)) // Rounded corners for the context menu animation.
|
||||
.contextMenu {
|
||||
context.viewState.contextMenuActionProvider?(timelineItem.id).map { actions in
|
||||
TimelineItemContextMenu(itemID: timelineItem.id, contextMenuActions: actions)
|
||||
@@ -105,7 +105,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
content()
|
||||
.bubbleStyle(inset: false,
|
||||
cornerRadius: cornerRadius,
|
||||
corners: timelineItem.roundedCorners)
|
||||
corners: roundedCorners)
|
||||
} else {
|
||||
VStack(alignment: .trailing, spacing: 4) {
|
||||
content()
|
||||
@@ -119,13 +119,13 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
.bubbleStyle(inset: true,
|
||||
color: timelineItem.isOutgoing ? .element.bubblesYou : .element.bubblesNotYou,
|
||||
cornerRadius: cornerRadius,
|
||||
corners: timelineItem.roundedCorners)
|
||||
corners: roundedCorners)
|
||||
}
|
||||
}
|
||||
|
||||
private var messageBubbleTopPadding: CGFloat {
|
||||
guard timelineItem.isOutgoing else { return 0 }
|
||||
return timelineItem.groupState == .single || timelineItem.groupState == .beginning ? 8 : 0
|
||||
return timelineStyleGrouping == .single || timelineStyleGrouping == .first ? 8 : 0
|
||||
}
|
||||
|
||||
private var shouldAvoidBubbling: Bool {
|
||||
@@ -135,6 +135,31 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
private var alignment: HorizontalAlignment {
|
||||
timelineItem.isOutgoing ? .trailing : .leading
|
||||
}
|
||||
|
||||
private var roundedCorners: UIRectCorner {
|
||||
switch timelineStyleGrouping {
|
||||
case .single:
|
||||
return .allCorners
|
||||
case .first:
|
||||
if timelineItem.isOutgoing {
|
||||
return [.topLeft, .topRight, .bottomLeft]
|
||||
} else {
|
||||
return [.topLeft, .topRight, .bottomRight]
|
||||
}
|
||||
case .middle:
|
||||
return timelineItem.isOutgoing ? [.topLeft, .bottomLeft] : [.topRight, .bottomRight]
|
||||
case .last:
|
||||
if timelineItem.isOutgoing {
|
||||
return [.topLeft, .bottomLeft, .bottomRight]
|
||||
} else {
|
||||
return [.topRight, .bottomLeft, .bottomRight]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var shouldShowSenderDetails: Bool {
|
||||
timelineStyleGrouping.shouldShowSenderDetails
|
||||
}
|
||||
}
|
||||
|
||||
private extension View {
|
||||
@@ -152,7 +177,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(1..<MockRoomTimelineController().timelineItems.count, id: \.self) { index in
|
||||
let item = MockRoomTimelineController().timelineItems[index]
|
||||
RoomTimelineViewFactory().buildTimelineViewFor(timelineItem: item)
|
||||
RoomTimelineViewProvider(timelineItem: item, grouping: .single)
|
||||
.padding(TimelineStyle.bubbles.rowInsets) // Insets added in the table view cells
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import SwiftUI
|
||||
|
||||
struct TimelineItemPlainStylerView<Content: View>: View {
|
||||
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||
@Environment(\.timelineGroupStyle) private var timelineStyleGrouping
|
||||
|
||||
let timelineItem: EventBasedTimelineItemProtocol
|
||||
@ViewBuilder let content: () -> Content
|
||||
@@ -44,7 +45,7 @@ struct TimelineItemPlainStylerView<Content: View>: View {
|
||||
|
||||
@ViewBuilder
|
||||
private var header: some View {
|
||||
if timelineItem.shouldShowSenderDetails {
|
||||
if shouldShowSenderDetails {
|
||||
HStack {
|
||||
TimelineSenderAvatarView(timelineItem: timelineItem)
|
||||
Text(timelineItem.sender.displayName ?? timelineItem.sender.id)
|
||||
@@ -78,6 +79,10 @@ struct TimelineItemPlainStylerView<Content: View>: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var shouldShowSenderDetails: Bool {
|
||||
timelineStyleGrouping.shouldShowSenderDetails
|
||||
}
|
||||
}
|
||||
|
||||
struct TimelineItemPlainStylerView_Previews: PreviewProvider {
|
||||
@@ -87,7 +92,7 @@ struct TimelineItemPlainStylerView_Previews: PreviewProvider {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(1..<MockRoomTimelineController().timelineItems.count, id: \.self) { index in
|
||||
let item = MockRoomTimelineController().timelineItems[index]
|
||||
RoomTimelineViewFactory().buildTimelineViewFor(timelineItem: item)
|
||||
RoomTimelineViewProvider(timelineItem: item, grouping: .single)
|
||||
.padding(TimelineStyle.plain.rowInsets) // Insets added in the table view cells
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,21 +32,50 @@ enum TimelineStyle: String, CaseIterable {
|
||||
}
|
||||
}
|
||||
|
||||
enum TimelineGroupStyle: Hashable {
|
||||
case single
|
||||
case first
|
||||
case middle
|
||||
case last
|
||||
|
||||
var shouldShowSenderDetails: Bool {
|
||||
switch self {
|
||||
case .single, .first:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Environment
|
||||
|
||||
private struct TimelineStyleKey: EnvironmentKey {
|
||||
static let defaultValue = TimelineStyle.bubbles
|
||||
}
|
||||
|
||||
private struct TimelineGroupStyleKey: EnvironmentKey {
|
||||
static let defaultValue = TimelineGroupStyle.single
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var timelineStyle: TimelineStyle {
|
||||
get { self[TimelineStyleKey.self] }
|
||||
set { self[TimelineStyleKey.self] = newValue }
|
||||
}
|
||||
|
||||
var timelineGroupStyle: TimelineGroupStyle {
|
||||
get { self[TimelineGroupStyleKey.self] }
|
||||
set { self[TimelineGroupStyleKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func timelineStyle(_ style: TimelineStyle) -> some View {
|
||||
environment(\.timelineStyle, style)
|
||||
}
|
||||
|
||||
func timelineStyleGrouping(_ grouping: TimelineGroupStyle) -> some View {
|
||||
environment(\.timelineGroupStyle, grouping)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ struct AudioRoomTimelineView_Previews: PreviewProvider {
|
||||
AudioRoomTimelineView(timelineItem: AudioRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "audio.ogg",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// 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 SwiftUI
|
||||
|
||||
struct CollapsibleRoomTimelineView: View {
|
||||
private let timelineItem: CollapsibleTimelineItem
|
||||
private let timelineViews: [RoomTimelineViewProvider]
|
||||
|
||||
@State private var isExpanded = false
|
||||
|
||||
init(timelineItem: CollapsibleTimelineItem) {
|
||||
self.timelineItem = timelineItem
|
||||
timelineViews = timelineItem.items.map { RoomTimelineViewProvider(timelineItem: $0, grouping: .single) }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
DisclosureGroup(ElementL10n.roomTimelineStateChanges(timelineItem.items.count),
|
||||
isExpanded: $isExpanded) {
|
||||
Group {
|
||||
ForEach(timelineViews) { timelineView in
|
||||
timelineView.body
|
||||
}
|
||||
}.transition(.opacity.animation(.elementDefault))
|
||||
}
|
||||
.disclosureGroupStyle(CollapsibleRoomTimelineItemDisclosureGroupStyle())
|
||||
.transaction { transaction in
|
||||
transaction.animation = .noAnimation // Fixes weird animations on the disclosure indicator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CollapsibleRoomTimelineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let item = CollapsibleTimelineItem(items: [SeparatorRoomTimelineItem(text: "This is a separator"),
|
||||
SeparatorRoomTimelineItem(text: "This is another separator")])
|
||||
CollapsibleRoomTimelineView(timelineItem: item)
|
||||
}
|
||||
}
|
||||
|
||||
private struct CollapsibleRoomTimelineItemDisclosureGroupStyle: DisclosureGroupStyle {
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
VStack {
|
||||
HStack(alignment: .center) {
|
||||
configuration.label
|
||||
Text(Image(systemName: "chevron.forward"))
|
||||
.rotationEffect(.degrees(configuration.isExpanded ? 90 : 0))
|
||||
.animation(.elementDefault, value: configuration.isExpanded)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.font(.element.footnote)
|
||||
.foregroundColor(.element.secondaryContent)
|
||||
.padding(.horizontal, 36)
|
||||
.padding(.vertical, 8)
|
||||
.onTapGesture {
|
||||
configuration.isExpanded.toggle()
|
||||
}
|
||||
|
||||
if configuration.isExpanded {
|
||||
configuration.content
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,6 @@ struct EmoteRoomTimelineView_Previews: PreviewProvider {
|
||||
EmoteRoomTimelineItem(id: UUID().uuidString,
|
||||
body: text,
|
||||
timestamp: timestamp,
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: senderId))
|
||||
|
||||
@@ -69,7 +69,6 @@ struct EncryptedRoomTimelineView_Previews: PreviewProvider {
|
||||
body: text,
|
||||
encryptionType: .unknown,
|
||||
timestamp: timestamp,
|
||||
groupState: .single,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: false,
|
||||
sender: .init(id: senderId))
|
||||
|
||||
@@ -46,7 +46,6 @@ struct FileRoomTimelineView_Previews: PreviewProvider {
|
||||
FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "document.pdf",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
@@ -56,7 +55,6 @@ struct FileRoomTimelineView_Previews: PreviewProvider {
|
||||
FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "document.docx",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
@@ -66,7 +64,6 @@ struct FileRoomTimelineView_Previews: PreviewProvider {
|
||||
FileRoomTimelineView(timelineItem: FileRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "document.txt",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
|
||||
@@ -74,7 +74,6 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some image",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
@@ -83,7 +82,6 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some other image",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
@@ -92,7 +90,6 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Blurhashed image",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
|
||||
@@ -66,7 +66,6 @@ struct NoticeRoomTimelineView_Previews: PreviewProvider {
|
||||
NoticeRoomTimelineItem(id: UUID().uuidString,
|
||||
body: text,
|
||||
timestamp: timestamp,
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: senderId))
|
||||
|
||||
@@ -41,25 +41,23 @@ struct ReadMarkerRoomTimelineView_Previews: PreviewProvider {
|
||||
static let item = ReadMarkerRoomTimelineItem()
|
||||
static var previews: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
RoomTimelineViewProvider.separator(.init(text: "Today"))
|
||||
RoomTimelineViewProvider.separator(.init(text: "Today"), .single)
|
||||
RoomTimelineViewProvider.text(.init(id: "",
|
||||
body: "This is another message",
|
||||
timestamp: "",
|
||||
groupState: .single,
|
||||
isOutgoing: true,
|
||||
isEditable: false,
|
||||
sender: .init(id: "1", displayName: "Bob")))
|
||||
sender: .init(id: "1", displayName: "Bob")), .single)
|
||||
|
||||
ReadMarkerRoomTimelineView(timelineItem: item)
|
||||
|
||||
RoomTimelineViewProvider.separator(.init(text: "Today"))
|
||||
RoomTimelineViewProvider.separator(.init(text: "Today"), .single)
|
||||
RoomTimelineViewProvider.text(.init(id: "",
|
||||
body: "This is a message",
|
||||
timestamp: "",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "", displayName: "Alice")))
|
||||
sender: .init(id: "", displayName: "Alice")), .single)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.environmentObject(viewModel.context)
|
||||
|
||||
@@ -45,7 +45,6 @@ struct RedactedRoomTimelineView_Previews: PreviewProvider {
|
||||
RedactedRoomTimelineItem(id: UUID().uuidString,
|
||||
body: text,
|
||||
timestamp: timestamp,
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: senderId))
|
||||
|
||||
@@ -43,7 +43,6 @@ struct StateRoomTimelineView_Previews: PreviewProvider {
|
||||
static let item = StateRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Alice joined",
|
||||
timestamp: "Now",
|
||||
groupState: .beginning,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: ""))
|
||||
|
||||
@@ -59,7 +59,6 @@ struct StickerRoomTimelineView_Previews: PreviewProvider {
|
||||
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some image",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
@@ -68,7 +67,6 @@ struct StickerRoomTimelineView_Previews: PreviewProvider {
|
||||
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some other image",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
@@ -77,7 +75,6 @@ struct StickerRoomTimelineView_Previews: PreviewProvider {
|
||||
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Blurhashed image",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
|
||||
@@ -67,7 +67,6 @@ struct TextRoomTimelineView_Previews: PreviewProvider {
|
||||
TextRoomTimelineItem(id: UUID().uuidString,
|
||||
body: text,
|
||||
timestamp: timestamp,
|
||||
groupState: .single,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: isOutgoing,
|
||||
sender: .init(id: senderId))
|
||||
|
||||
@@ -66,7 +66,6 @@ struct UnsupportedRoomTimelineView_Previews: PreviewProvider {
|
||||
eventType: "Some Event Type",
|
||||
error: "Something went wrong",
|
||||
timestamp: timestamp,
|
||||
groupState: .single,
|
||||
isOutgoing: isOutgoing,
|
||||
isEditable: false,
|
||||
sender: .init(id: senderId))
|
||||
|
||||
@@ -69,7 +69,6 @@ struct VideoRoomTimelineView_Previews: PreviewProvider {
|
||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some video",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
@@ -80,7 +79,6 @@ struct VideoRoomTimelineView_Previews: PreviewProvider {
|
||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some other video",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
@@ -91,7 +89,6 @@ struct VideoRoomTimelineView_Previews: PreviewProvider {
|
||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Blurhashed video",
|
||||
timestamp: "Now",
|
||||
groupState: .single,
|
||||
isOutgoing: false,
|
||||
isEditable: false,
|
||||
sender: .init(id: "Bob"),
|
||||
|
||||
@@ -83,7 +83,6 @@ struct TimelineView: UIViewControllerRepresentable {
|
||||
|
||||
struct TimelineTableView_Previews: PreviewProvider {
|
||||
static let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
|
||||
timelineViewFactory: RoomTimelineViewFactory(),
|
||||
mediaProvider: MockMediaProvider(),
|
||||
roomName: "Preview room")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user