Remove unused SwiftUI timeline version

This commit is contained in:
Stefan Ceriu
2024-02-02 17:22:37 +02:00
committed by Stefan Ceriu
parent e8d5f00903
commit e42b1c2a68
8 changed files with 6 additions and 279 deletions

View File

@@ -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 */,

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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")

View File

@@ -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())
}
}
}

View File

@@ -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 }

View File

@@ -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") {