mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Introduce a new SwiftUI TimelineView layer (wrapping the representable) to hold all the timeline specific bindings that are currently duplicated throughout the screens a timeline is used.
This commit is contained in:
committed by
Stefan Ceriu
parent
4bcdf58e3b
commit
d058e7cd25
+1
-24
@@ -28,26 +28,6 @@ struct PinnedEventsTimelineScreen: View {
|
|||||||
.background(.compound.bgCanvasDefault)
|
.background(.compound.bgCanvasDefault)
|
||||||
.interactiveDismissDisabled()
|
.interactiveDismissDisabled()
|
||||||
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
||||||
.sheet(item: $timelineContext.manageMemberViewModel) {
|
|
||||||
ManageRoomMemberSheetView(context: $0.context)
|
|
||||||
}
|
|
||||||
.sheet(item: $timelineContext.debugInfo) { TimelineItemDebugView(info: $0) }
|
|
||||||
.sheet(item: $timelineContext.actionMenuInfo) { info in
|
|
||||||
let actions = TimelineItemMenuActionProvider(timelineItem: info.item,
|
|
||||||
canCurrentUserRedactSelf: timelineContext.viewState.canCurrentUserRedactSelf,
|
|
||||||
canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers,
|
|
||||||
canCurrentUserPin: timelineContext.viewState.canCurrentUserPin,
|
|
||||||
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
|
|
||||||
isDM: timelineContext.viewState.isDirectOneToOneRoom,
|
|
||||||
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
|
|
||||||
timelineKind: timelineContext.viewState.timelineKind,
|
|
||||||
emojiProvider: timelineContext.viewState.emojiProvider)
|
|
||||||
.makeActions()
|
|
||||||
if let actions {
|
|
||||||
TimelineItemMenu(item: info.item, actions: actions)
|
|
||||||
.environmentObject(timelineContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@@ -68,10 +48,7 @@ struct PinnedEventsTimelineScreen: View {
|
|||||||
.padding(.top, 48)
|
.padding(.top, 48)
|
||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
} else {
|
} else {
|
||||||
TimelineView()
|
TimelineView(timelineContext: timelineContext)
|
||||||
.id(timelineContext.viewState.roomID)
|
|
||||||
.environmentObject(timelineContext)
|
|
||||||
.environment(\.focussedEventID, timelineContext.viewState.timelineState.focussedEvent?.eventID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,10 @@ struct RoomScreen: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
timeline
|
TimelineView(timelineContext: timelineContext)
|
||||||
|
.overlay(alignment: .bottomTrailing) {
|
||||||
|
scrollToBottomButton
|
||||||
|
}
|
||||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||||
.overlay(alignment: .top) {
|
.overlay(alignment: .top) {
|
||||||
pinnedItemsBanner
|
pinnedItemsBanner
|
||||||
@@ -65,38 +68,6 @@ struct RoomScreen: View {
|
|||||||
.toolbar { toolbar }
|
.toolbar { toolbar }
|
||||||
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
.toolbarBackground(.visible, for: .navigationBar) // Fix the toolbar's background.
|
||||||
.overlay { loadingIndicator }
|
.overlay { loadingIndicator }
|
||||||
.alert(item: $timelineContext.alertInfo)
|
|
||||||
.sheet(item: $timelineContext.manageMemberViewModel) {
|
|
||||||
ManageRoomMemberSheetView(context: $0.context)
|
|
||||||
}
|
|
||||||
.sheet(item: $timelineContext.debugInfo) { TimelineItemDebugView(info: $0) }
|
|
||||||
.sheet(item: $timelineContext.actionMenuInfo) { info in
|
|
||||||
let actions = TimelineItemMenuActionProvider(timelineItem: info.item,
|
|
||||||
canCurrentUserRedactSelf: timelineContext.viewState.canCurrentUserRedactSelf,
|
|
||||||
canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers,
|
|
||||||
canCurrentUserPin: timelineContext.viewState.canCurrentUserPin,
|
|
||||||
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
|
|
||||||
isDM: timelineContext.viewState.isDirectOneToOneRoom,
|
|
||||||
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
|
|
||||||
timelineKind: timelineContext.viewState.timelineKind,
|
|
||||||
emojiProvider: timelineContext.viewState.emojiProvider)
|
|
||||||
.makeActions()
|
|
||||||
if let actions {
|
|
||||||
TimelineItemMenu(item: info.item, actions: actions)
|
|
||||||
.environmentObject(timelineContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sheet(item: $timelineContext.reactionSummaryInfo) {
|
|
||||||
ReactionsSummaryView(reactions: $0.reactions,
|
|
||||||
members: timelineContext.viewState.members,
|
|
||||||
mediaProvider: timelineContext.mediaProvider,
|
|
||||||
selectedReactionKey: $0.selectedKey)
|
|
||||||
.edgesIgnoringSafeArea([.bottom])
|
|
||||||
}
|
|
||||||
.sheet(item: $timelineContext.readReceiptsSummaryInfo) {
|
|
||||||
ReadReceiptsSummaryView(orderedReadReceipts: $0.orderedReceipts)
|
|
||||||
.environmentObject(timelineContext)
|
|
||||||
}
|
|
||||||
.timelineMediaPreview(viewModel: $roomContext.mediaPreviewViewModel)
|
.timelineMediaPreview(viewModel: $roomContext.mediaPreviewViewModel)
|
||||||
.track(screen: .Room)
|
.track(screen: .Room)
|
||||||
.onDrop(of: ["public.item", "public.file-url"], isTargeted: $dragOver) { providers -> Bool in
|
.onDrop(of: ["public.item", "public.file-url"], isTargeted: $dragOver) { providers -> Bool in
|
||||||
@@ -111,16 +82,6 @@ struct RoomScreen: View {
|
|||||||
.sentryTrace("\(Self.self)")
|
.sentryTrace("\(Self.self)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private var timeline: some View {
|
|
||||||
TimelineView()
|
|
||||||
.id(timelineContext.viewState.roomID)
|
|
||||||
.environmentObject(timelineContext)
|
|
||||||
.environment(\.focussedEventID, timelineContext.viewState.timelineState.focussedEvent?.eventID)
|
|
||||||
.overlay(alignment: .bottomTrailing) {
|
|
||||||
scrollToBottomButton
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var pinnedItemsBanner: some View {
|
private var pinnedItemsBanner: some View {
|
||||||
Group {
|
Group {
|
||||||
|
|||||||
@@ -13,39 +13,11 @@ struct ThreadTimelineScreen: View {
|
|||||||
@ObservedObject var timelineContext: TimelineViewModel.Context
|
@ObservedObject var timelineContext: TimelineViewModel.Context
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
content
|
TimelineView(timelineContext: timelineContext)
|
||||||
.navigationTitle("Thread")
|
.navigationTitle("Thread")
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.background(.compound.bgCanvasDefault)
|
.background(.compound.bgCanvasDefault)
|
||||||
.interactiveDismissDisabled()
|
.interactiveDismissDisabled()
|
||||||
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
.timelineMediaPreview(viewModel: $context.mediaPreviewViewModel)
|
||||||
.sheet(item: $timelineContext.manageMemberViewModel) {
|
|
||||||
ManageRoomMemberSheetView(context: $0.context)
|
|
||||||
}
|
|
||||||
.sheet(item: $timelineContext.debugInfo) { TimelineItemDebugView(info: $0) }
|
|
||||||
.sheet(item: $timelineContext.actionMenuInfo) { info in
|
|
||||||
let actions = TimelineItemMenuActionProvider(timelineItem: info.item,
|
|
||||||
canCurrentUserRedactSelf: timelineContext.viewState.canCurrentUserRedactSelf,
|
|
||||||
canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers,
|
|
||||||
canCurrentUserPin: timelineContext.viewState.canCurrentUserPin,
|
|
||||||
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
|
|
||||||
isDM: timelineContext.viewState.isDirectOneToOneRoom,
|
|
||||||
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
|
|
||||||
timelineKind: timelineContext.viewState.timelineKind,
|
|
||||||
emojiProvider: timelineContext.viewState.emojiProvider)
|
|
||||||
.makeActions()
|
|
||||||
if let actions {
|
|
||||||
TimelineItemMenu(item: info.item, actions: actions)
|
|
||||||
.environmentObject(timelineContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private var content: some View {
|
|
||||||
TimelineView()
|
|
||||||
.id(timelineContext.viewState.roomID)
|
|
||||||
.environmentObject(timelineContext)
|
|
||||||
.environment(\.focussedEventID, timelineContext.viewState.timelineState.focussedEvent?.eventID)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class TypingMembersObservableObject: ObservableObject {
|
|||||||
/// extra keyboard handling magic that wasn't playing well with SwiftUI (as of iOS 16.1).
|
/// extra keyboard handling magic that wasn't playing well with SwiftUI (as of iOS 16.1).
|
||||||
/// Also this TableViewController uses a **flipped tableview**
|
/// Also this TableViewController uses a **flipped tableview**
|
||||||
class TimelineTableViewController: UIViewController {
|
class TimelineTableViewController: UIViewController {
|
||||||
private let coordinator: TimelineView.Coordinator
|
private let coordinator: TimelineViewRepresentable.Coordinator
|
||||||
private let tableView = UITableView(frame: .zero, style: .plain)
|
private let tableView = UITableView(frame: .zero, style: .plain)
|
||||||
|
|
||||||
var timelineItemsDictionary = OrderedDictionary<TimelineItemIdentifier.UniqueID, RoomTimelineItemViewState>() {
|
var timelineItemsDictionary = OrderedDictionary<TimelineItemIdentifier.UniqueID, RoomTimelineItemViewState>() {
|
||||||
@@ -168,7 +168,7 @@ class TimelineTableViewController: UIViewController {
|
|||||||
/// Whether or not the view has been shown on screen yet.
|
/// Whether or not the view has been shown on screen yet.
|
||||||
private var hasAppearedOnce = false
|
private var hasAppearedOnce = false
|
||||||
|
|
||||||
init(coordinator: TimelineView.Coordinator,
|
init(coordinator: TimelineViewRepresentable.Coordinator,
|
||||||
isScrolledToBottom: Binding<Bool>,
|
isScrolledToBottom: Binding<Bool>,
|
||||||
scrollToBottomPublisher: PassthroughSubject<Void, Never>) {
|
scrollToBottomPublisher: PassthroughSubject<Void, Never>) {
|
||||||
self.coordinator = coordinator
|
self.coordinator = coordinator
|
||||||
|
|||||||
@@ -8,8 +8,51 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import WysiwygComposer
|
import WysiwygComposer
|
||||||
|
|
||||||
|
struct TimelineView: View {
|
||||||
|
@ObservedObject var timelineContext: TimelineViewModel.Context
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
TimelineViewRepresentable()
|
||||||
|
.id(timelineContext.viewState.roomID)
|
||||||
|
.environment(\.focussedEventID, timelineContext.viewState.timelineState.focussedEvent?.eventID)
|
||||||
|
.alert(item: $timelineContext.alertInfo)
|
||||||
|
.sheet(item: $timelineContext.manageMemberViewModel) {
|
||||||
|
ManageRoomMemberSheetView(context: $0.context)
|
||||||
|
}
|
||||||
|
.sheet(item: $timelineContext.debugInfo) { TimelineItemDebugView(info: $0) }
|
||||||
|
.sheet(item: $timelineContext.actionMenuInfo) { info in
|
||||||
|
let actions = TimelineItemMenuActionProvider(timelineItem: info.item,
|
||||||
|
canCurrentUserRedactSelf: timelineContext.viewState.canCurrentUserRedactSelf,
|
||||||
|
canCurrentUserRedactOthers: timelineContext.viewState.canCurrentUserRedactOthers,
|
||||||
|
canCurrentUserPin: timelineContext.viewState.canCurrentUserPin,
|
||||||
|
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
|
||||||
|
isDM: timelineContext.viewState.isDirectOneToOneRoom,
|
||||||
|
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
|
||||||
|
timelineKind: timelineContext.viewState.timelineKind,
|
||||||
|
emojiProvider: timelineContext.viewState.emojiProvider)
|
||||||
|
.makeActions()
|
||||||
|
if let actions {
|
||||||
|
TimelineItemMenu(item: info.item, actions: actions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sheet(item: $timelineContext.reactionSummaryInfo) {
|
||||||
|
ReactionsSummaryView(reactions: $0.reactions,
|
||||||
|
members: timelineContext.viewState.members,
|
||||||
|
mediaProvider: timelineContext.mediaProvider,
|
||||||
|
selectedReactionKey: $0.selectedKey)
|
||||||
|
.edgesIgnoringSafeArea([.bottom])
|
||||||
|
}
|
||||||
|
.sheet(item: $timelineContext.readReceiptsSummaryInfo) {
|
||||||
|
ReadReceiptsSummaryView(orderedReadReceipts: $0.orderedReceipts)
|
||||||
|
}
|
||||||
|
// Ensure these are available in the sheets as well. The order is important.
|
||||||
|
.environmentObject(timelineContext)
|
||||||
|
.environment(\.timelineContext, timelineContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A table view wrapper that displays the timeline of a room.
|
/// A table view wrapper that displays the timeline of a room.
|
||||||
struct TimelineView: UIViewControllerRepresentable {
|
struct TimelineViewRepresentable: UIViewControllerRepresentable {
|
||||||
@EnvironmentObject private var viewModelContext: TimelineViewModel.Context
|
@EnvironmentObject private var viewModelContext: TimelineViewModel.Context
|
||||||
@Environment(\.openURL) var openURL
|
@Environment(\.openURL) var openURL
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user