Remove unused SwiftUI timeline version
This commit is contained in:
committed by
Stefan Ceriu
parent
e8d5f00903
commit
e42b1c2a68
@@ -315,7 +315,6 @@
|
||||
4FDC8A9764CFDA90CE035725 /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FB2253D36E81E045E1CB432 /* Duration.swift */; };
|
||||
4FF90E2242DBD596E1ED2E27 /* AppCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */; };
|
||||
4FFDC274824F7CC0BBDF581E /* BugReportScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C2BCE0BC1FC69C1B36E688 /* BugReportScreenModels.swift */; };
|
||||
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874A1842477895F199567BD7 /* TimelineView.swift */; };
|
||||
50381244BA280451771BE3ED /* PINTextFieldTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF13BFD415CA84B1272E94F8 /* PINTextFieldTests.swift */; };
|
||||
50539366B408780B232C1910 /* EstimatedWaveformView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD0FF64B0E6470F66F42E182 /* EstimatedWaveformView.swift */; };
|
||||
50C90117FE25390BFBD40173 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
||||
@@ -1558,7 +1557,6 @@
|
||||
86376BEE425704AEE197CA54 /* PillContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillContext.swift; sourceTree = "<group>"; };
|
||||
86873A768B13069BB5CAECF6 /* InvitesScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
86A6F283BC574FDB96ABBB07 /* DeveloperOptionsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
874A1842477895F199567BD7 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = "<group>"; };
|
||||
88410BD213FDF9B28E8B671F /* UserDetailsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDetailsEditScreen.swift; sourceTree = "<group>"; };
|
||||
8896CDD20CA2D87EA3B848A1 /* RoomNotificationSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreen.swift; sourceTree = "<group>"; };
|
||||
889DEDD63C68ABDA8AD29812 /* VoiceMessageMediaManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageMediaManagerProtocol.swift; sourceTree = "<group>"; };
|
||||
@@ -3514,7 +3512,6 @@
|
||||
4A4AD793D50748F8997E5B15 /* TimelineItemMacContextMenu.swift */,
|
||||
A1BF12A5E7C76777C4BF0F2B /* TimelineItemMenu.swift */,
|
||||
0BC588051E6572A1AF51D738 /* TimelineSenderAvatarView.swift */,
|
||||
874A1842477895F199567BD7 /* TimelineView.swift */,
|
||||
45778D52AECD4EB99A289214 /* Polls */,
|
||||
4820FFB9F4FDDFD95763D498 /* ReadReceipts */,
|
||||
1D8572B713A11CFDBF009B2F /* Replies */,
|
||||
@@ -5990,7 +5987,6 @@
|
||||
69BCBB4FB2DC3D61A28D3FD8 /* TimelineStyle.swift in Sources */,
|
||||
FFD3E4FF948E06C7585317FC /* TimelineStyler.swift in Sources */,
|
||||
2B1E080B32167AE9EFC763A2 /* TimelineTableViewController.swift in Sources */,
|
||||
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */,
|
||||
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */,
|
||||
A37EED79941AD3B7140B3822 /* UIDevice.swift in Sources */,
|
||||
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */,
|
||||
|
||||
@@ -45,7 +45,6 @@ final class AppSettings {
|
||||
// Feature flags
|
||||
case shouldCollapseRoomStateEvents
|
||||
case userSuggestionsEnabled
|
||||
case swiftUITimelineEnabled
|
||||
case mentionsBadgeEnabled
|
||||
case roomListFiltersEnabled
|
||||
}
|
||||
@@ -270,9 +269,6 @@ final class AppSettings {
|
||||
@UserPreference(key: UserDefaultsKeys.userSuggestionsEnabled, defaultValue: false, storageType: .volatile)
|
||||
var userSuggestionsEnabled
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.swiftUITimelineEnabled, defaultValue: false, storageType: .volatile)
|
||||
var swiftUITimelineEnabled
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.mentionsBadgeEnabled, defaultValue: true, storageType: .userDefaults(store))
|
||||
var mentionsBadgeEnabled
|
||||
|
||||
|
||||
@@ -99,8 +99,6 @@ enum RoomScreenViewAction {
|
||||
|
||||
case showReadReceipts(itemID: TimelineItemIdentifier)
|
||||
|
||||
case scrolledToBottom
|
||||
|
||||
case poll(RoomScreenViewPollAction)
|
||||
|
||||
case audio(RoomScreenViewAudioAction)
|
||||
@@ -124,7 +122,6 @@ struct RoomScreenViewState: BindableState {
|
||||
var timelineStyle: TimelineStyle
|
||||
var isEncryptedOneToOneRoom = false
|
||||
var timelineViewState = TimelineViewState() // check the doc before changing this
|
||||
var swiftUITimelineEnabled = false
|
||||
|
||||
var ownUserID: String
|
||||
|
||||
@@ -222,14 +219,11 @@ struct TimelineViewState {
|
||||
|
||||
var itemsDictionary = OrderedDictionary<String, RoomTimelineItemViewState>()
|
||||
|
||||
var renderedTimelineIDs = [String]()
|
||||
var pendingTimelineIDs = [String]()
|
||||
|
||||
var timelineIDs: [String] {
|
||||
itemsDictionary.keys.elements
|
||||
}
|
||||
|
||||
var itemViewStates: [RoomTimelineItemViewState] {
|
||||
renderedTimelineIDs.compactMap { itemsDictionary[$0] }
|
||||
itemsDictionary.values.elements
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,10 +162,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
Task { await timelineController.cancelSending(itemID: itemID) }
|
||||
case .paginateBackwards:
|
||||
paginateBackwards()
|
||||
case .scrolledToBottom:
|
||||
if state.swiftUITimelineEnabled {
|
||||
renderPendingTimelineItems()
|
||||
}
|
||||
case .poll(let pollAction):
|
||||
processPollAction(pollAction)
|
||||
case .audio(let audioAction):
|
||||
@@ -267,10 +263,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
|
||||
private func setupSubscriptions() {
|
||||
appSettings.$swiftUITimelineEnabled
|
||||
.weakAssign(to: \.state.swiftUITimelineEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
timelineController.callbacks
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] callback in
|
||||
@@ -551,13 +543,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
}
|
||||
|
||||
// The SwiftUI scroll view needs special handling, see `selectivelyUpdateTimelineItems`
|
||||
if state.swiftUITimelineEnabled {
|
||||
selectivelyUpdateTimelineItems(timelineItemsDictionary: timelineItemsDictionary)
|
||||
} else {
|
||||
state.timelineViewState.itemsDictionary = timelineItemsDictionary
|
||||
state.timelineViewState.renderedTimelineIDs = Array(timelineItemsDictionary.keys)
|
||||
}
|
||||
state.timelineViewState.itemsDictionary = timelineItemsDictionary
|
||||
}
|
||||
|
||||
private func updateViewState(item: RoomTimelineItemProtocol, groupStyle: TimelineGroupStyle) -> RoomTimelineItemViewState {
|
||||
@@ -569,41 +555,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
return RoomTimelineItemViewState(item: item, groupStyle: groupStyle)
|
||||
}
|
||||
}
|
||||
|
||||
/// With the timeline scroll being reversed, introducing items at it's top (i.e. bottom now) will make the content move upwards, which is unwanted when
|
||||
/// reading history. Delay rendering new items until it reaches the bottom again.
|
||||
private func selectivelyUpdateTimelineItems(timelineItemsDictionary: OrderedDictionary<String, RoomTimelineItemViewState>) {
|
||||
var timelineViewState = state.timelineViewState
|
||||
|
||||
let newItemIdentifiers = Array(timelineItemsDictionary.keys)
|
||||
|
||||
if !state.bindings.isScrolledToBottom,
|
||||
let lastItemIdentifier = state.timelineViewState.renderedTimelineIDs.last,
|
||||
let newLastItemIdentifierIndex = newItemIdentifiers.firstIndex(where: { $0 == lastItemIdentifier }) {
|
||||
timelineViewState.pendingTimelineIDs = Array(newItemIdentifiers.dropFirst(newLastItemIdentifierIndex + 1))
|
||||
timelineViewState.renderedTimelineIDs = Array(newItemIdentifiers.dropLast(newItemIdentifiers.count - (newLastItemIdentifierIndex + 1)))
|
||||
} else {
|
||||
// Otherwise just render everything normally
|
||||
timelineViewState.renderedTimelineIDs = Array(timelineItemsDictionary.keys)
|
||||
}
|
||||
|
||||
timelineViewState.itemsDictionary = timelineItemsDictionary
|
||||
|
||||
state.timelineViewState = timelineViewState
|
||||
}
|
||||
|
||||
private func renderPendingTimelineItems() {
|
||||
// Render pending timeline items when the scroll view reaches the bottom again
|
||||
guard state.bindings.isScrolledToBottom,
|
||||
state.timelineViewState.pendingTimelineIDs.count > 0 else {
|
||||
return
|
||||
}
|
||||
|
||||
var newTimelineViewState = state.timelineViewState
|
||||
newTimelineViewState.renderedTimelineIDs = state.timelineViewState.renderedTimelineIDs + state.timelineViewState.pendingTimelineIDs
|
||||
newTimelineViewState.pendingTimelineIDs = []
|
||||
state.timelineViewState = newTimelineViewState
|
||||
}
|
||||
|
||||
private func canGroupItem(timelineItem: RoomTimelineItemProtocol, with otherTimelineItem: RoomTimelineItemProtocol) -> Bool {
|
||||
if timelineItem is CollapsibleTimelineItem || otherTimelineItem is CollapsibleTimelineItem {
|
||||
|
||||
@@ -88,35 +88,18 @@ struct RoomScreen: View {
|
||||
context.send(viewAction: .cancelSend(itemID: info.itemID))
|
||||
}
|
||||
}
|
||||
.onChange(of: context.isScrolledToBottom) { isScrolledToBottom in
|
||||
if isScrolledToBottom {
|
||||
context.send(viewAction: .scrolledToBottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var timeline: some View {
|
||||
timelineSwitch
|
||||
UITimelineView()
|
||||
.id(context.viewState.roomID)
|
||||
.environmentObject(context)
|
||||
.environment(\.timelineStyle, context.viewState.timelineStyle)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var timelineSwitch: some View {
|
||||
if context.viewState.swiftUITimelineEnabled {
|
||||
TimelineView(viewState: context.viewState.timelineViewState,
|
||||
isScrolledToBottom: $context.isScrolledToBottom) {
|
||||
context.send(viewAction: .paginateBackwards)
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
scrollToBottomButton
|
||||
}
|
||||
} else {
|
||||
UITimelineView()
|
||||
.overlay(alignment: .bottomTrailing) {
|
||||
scrollToBottomButton
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var scrollToBottomButton: some View {
|
||||
Button { context.viewState.timelineViewState.scrollToBottomPublisher.send(()) } label: {
|
||||
Image(systemName: "chevron.down")
|
||||
|
||||
@@ -1,187 +0,0 @@
|
||||
//
|
||||
// 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 Combine
|
||||
import SwiftUI
|
||||
|
||||
import SwiftUIIntrospect
|
||||
import WysiwygComposer
|
||||
|
||||
struct TimelineView: View {
|
||||
let viewState: TimelineViewState
|
||||
@Binding var isScrolledToBottom: Bool
|
||||
let paginationAction: () -> Void
|
||||
|
||||
@Environment(\.timelineStyle) private var timelineStyle
|
||||
|
||||
private let bottomID = "RoomTimelineBottomPinIdentifier"
|
||||
private let topID = "RoomTimelineTopPinIdentifier"
|
||||
|
||||
@State private var scrollViewAdapter = ScrollViewAdapter()
|
||||
@State private var paginateBackwardsPublisher = PassthroughSubject<Void, Never>()
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { scrollView in
|
||||
timelineScrollView
|
||||
.introspect(.scrollView, on: .supportedVersions) { uiScrollView in
|
||||
guard uiScrollView != scrollViewAdapter.scrollView else {
|
||||
return
|
||||
}
|
||||
|
||||
scrollViewAdapter.scrollView = uiScrollView
|
||||
scrollViewAdapter.shouldScrollToTopClosure = { _ in
|
||||
withElementAnimation {
|
||||
scrollView.scrollTo(topID)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Allows the scroll to top to work properly
|
||||
uiScrollView.contentOffset.y -= 1
|
||||
paginateBackwardsPublisher.send()
|
||||
}
|
||||
.scaleEffect(x: 1, y: -1)
|
||||
.onReceive(viewState.scrollToBottomPublisher) { _ in
|
||||
withElementAnimation {
|
||||
scrollView.scrollTo(bottomID)
|
||||
}
|
||||
}
|
||||
.scrollDismissesKeyboard(.immediately)
|
||||
}
|
||||
.overlay(scrollToBottomButton, alignment: .bottomTrailing)
|
||||
.onReceive(scrollViewAdapter.didScroll) { _ in
|
||||
guard let scrollView = scrollViewAdapter.scrollView else {
|
||||
return
|
||||
}
|
||||
let offset = scrollView.contentOffset.y + scrollView.contentInset.top
|
||||
|
||||
let isScrolledToBottom = offset <= 0
|
||||
|
||||
// Only update the binding on changes to avoid needlessly recomputing the hierarchy when scrolling.
|
||||
if self.isScrolledToBottom != isScrolledToBottom {
|
||||
self.isScrolledToBottom = isScrolledToBottom
|
||||
}
|
||||
|
||||
// Allows the scroll to top to work properly
|
||||
if offset == 0 {
|
||||
scrollView.contentOffset.y -= 1
|
||||
}
|
||||
|
||||
paginateBackwardsPublisher.send()
|
||||
}
|
||||
.onReceive(paginateBackwardsPublisher.collect(.byTime(DispatchQueue.main, 0.1))) { _ in
|
||||
paginateBackwardsIfNeeded()
|
||||
}
|
||||
.onAppear {
|
||||
paginateBackwardsPublisher.send()
|
||||
}
|
||||
}
|
||||
|
||||
private var timelineScrollView: some View {
|
||||
ScrollView {
|
||||
bottomPin
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(viewState.itemViewStates.reversed()) { viewState in
|
||||
RoomTimelineItemView(viewState: viewState)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(timelineStyle.rowInsets)
|
||||
.scaleEffect(x: 1, y: -1)
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: viewState.itemViewStates)
|
||||
topPin
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to mark the top of the scroll view and easily scroll to it
|
||||
private var topPin: some View {
|
||||
Divider()
|
||||
.id(topID)
|
||||
.hidden()
|
||||
.frame(height: 0)
|
||||
}
|
||||
|
||||
/// Used to mark the bottom of the scroll view and easily scroll to it
|
||||
private var bottomPin: some View {
|
||||
Divider()
|
||||
.id(bottomID)
|
||||
.hidden()
|
||||
.frame(height: 0)
|
||||
}
|
||||
|
||||
private var scrollToBottomButton: some View {
|
||||
Button {
|
||||
viewState.scrollToBottomPublisher.send()
|
||||
} label: {
|
||||
Image(systemName: "chevron.down")
|
||||
.font(.compound.bodyLG)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(.compound.iconSecondary)
|
||||
.padding(13)
|
||||
.offset(y: 1)
|
||||
.background {
|
||||
Circle()
|
||||
.fill(Color.compound.iconOnSolidPrimary)
|
||||
// Intentionally using system primary colour to get white/black.
|
||||
.shadow(color: .primary.opacity(0.33), radius: 2.0)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.opacity(isScrolledToBottom ? 0.0 : 1.0)
|
||||
.accessibilityHidden(isScrolledToBottom)
|
||||
.animation(.elementDefault, value: isScrolledToBottom)
|
||||
}
|
||||
|
||||
private func paginateBackwardsIfNeeded() {
|
||||
guard let scrollView = scrollViewAdapter.scrollView,
|
||||
viewState.canBackPaginate,
|
||||
!viewState.isBackPaginating else {
|
||||
return
|
||||
}
|
||||
|
||||
let visibleHeight = scrollView.visibleSize.height
|
||||
let contentHeight = scrollView.contentSize.height
|
||||
let offset = scrollView.contentOffset.y + scrollView.contentInset.top
|
||||
let threshold = contentHeight - visibleHeight * 2
|
||||
|
||||
guard offset > threshold else {
|
||||
return
|
||||
}
|
||||
|
||||
paginationAction()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct TimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = RoomScreenViewModel(roomProxy: RoomProxyMock(with: .init(displayName: "Preview room")),
|
||||
timelineController: MockRoomTimelineController(),
|
||||
mediaProvider: MockMediaProvider(),
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
application: ApplicationMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics,
|
||||
notificationCenter: NotificationCenterMock())
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
RoomScreen(context: viewModel.context, composerToolbar: ComposerToolbar.mock())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,6 @@ protocol DeveloperOptionsProtocol: AnyObject {
|
||||
var otlpTracingEnabled: Bool { get set }
|
||||
var shouldCollapseRoomStateEvents: Bool { get set }
|
||||
var userSuggestionsEnabled: Bool { get set }
|
||||
var swiftUITimelineEnabled: Bool { get set }
|
||||
var mentionsBadgeEnabled: Bool { get set }
|
||||
var roomListFiltersEnabled: Bool { get set }
|
||||
|
||||
|
||||
@@ -36,11 +36,6 @@ struct DeveloperOptionsScreen: View {
|
||||
Toggle(isOn: $context.shouldCollapseRoomStateEvents) {
|
||||
Text("Collapse room state events")
|
||||
}
|
||||
|
||||
Toggle(isOn: $context.swiftUITimelineEnabled) {
|
||||
Text("SwiftUI Timeline")
|
||||
Text("Resets on reboot")
|
||||
}
|
||||
}
|
||||
|
||||
Section("Room creation") {
|
||||
|
||||
Reference in New Issue
Block a user