From e42b1c2a685eff9b1d7af3200ac4874e09a978c9 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 2 Feb 2024 17:22:37 +0200 Subject: [PATCH] Remove unused SwiftUI timeline version --- ElementX.xcodeproj/project.pbxproj | 4 - .../Sources/Application/AppSettings.swift | 4 - .../Screens/RoomScreen/RoomScreenModels.swift | 8 +- .../RoomScreen/RoomScreenViewModel.swift | 51 +---- .../Screens/RoomScreen/View/RoomScreen.swift | 25 +-- .../RoomScreen/View/TimelineView.swift | 187 ------------------ .../DeveloperOptionsScreenModels.swift | 1 - .../View/DeveloperOptionsScreen.swift | 5 - 8 files changed, 6 insertions(+), 279 deletions(-) delete mode 100644 ElementX/Sources/Screens/RoomScreen/View/TimelineView.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b4830ad5d..ce5d2504a 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -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 = ""; }; 86873A768B13069BB5CAECF6 /* InvitesScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenViewModelProtocol.swift; sourceTree = ""; }; 86A6F283BC574FDB96ABBB07 /* DeveloperOptionsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenViewModel.swift; sourceTree = ""; }; - 874A1842477895F199567BD7 /* TimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineView.swift; sourceTree = ""; }; 88410BD213FDF9B28E8B671F /* UserDetailsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDetailsEditScreen.swift; sourceTree = ""; }; 8896CDD20CA2D87EA3B848A1 /* RoomNotificationSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreen.swift; sourceTree = ""; }; 889DEDD63C68ABDA8AD29812 /* VoiceMessageMediaManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageMediaManagerProtocol.swift; sourceTree = ""; }; @@ -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 */, diff --git a/ElementX/Sources/Application/AppSettings.swift b/ElementX/Sources/Application/AppSettings.swift index 53842c889..dbbba5292 100644 --- a/ElementX/Sources/Application/AppSettings.swift +++ b/ElementX/Sources/Application/AppSettings.swift @@ -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 diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift index 34088d18c..a0d3cd4ad 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenModels.swift @@ -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() - var renderedTimelineIDs = [String]() - var pendingTimelineIDs = [String]() - var timelineIDs: [String] { itemsDictionary.keys.elements } var itemViewStates: [RoomTimelineItemViewState] { - renderedTimelineIDs.compactMap { itemsDictionary[$0] } + itemsDictionary.values.elements } } diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index ed460ae87..503309e98 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -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) { - 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 { diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 262fa58a3..64e15ae7d 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -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") diff --git a/ElementX/Sources/Screens/RoomScreen/View/TimelineView.swift b/ElementX/Sources/Screens/RoomScreen/View/TimelineView.swift deleted file mode 100644 index 67488d49e..000000000 --- a/ElementX/Sources/Screens/RoomScreen/View/TimelineView.swift +++ /dev/null @@ -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() - - 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()) - } - } -} diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift index a8b7e99b3..f5002bf12 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/DeveloperOptionsScreenModels.swift @@ -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 } diff --git a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift index 05d086425..77c3fa4ff 100644 --- a/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift +++ b/ElementX/Sources/Screens/Settings/DeveloperOptionsScreen/View/DeveloperOptionsScreen.swift @@ -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") {