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 31ca8ed7f2
commit d6b7212b5a
8 changed files with 6 additions and 279 deletions
@@ -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())
}
}
}