Fix a bug where the timeline disappeared when VoiceOver was enabled. (#4701)
This commit is contained in:
@@ -164,6 +164,7 @@
|
||||
1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C8616254EE40CA8BA5E9BC2 /* VideoRoomTimelineItemContent.swift */; };
|
||||
1AB3D8563AB12635250A6A6E /* StaticLocationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15E0017717EAE3A1D02D005 /* StaticLocationScreenCoordinator.swift */; };
|
||||
1AE4AEA0FA8DEF52671832E0 /* RoomTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */; };
|
||||
1AF4A82B4332CAD2DB3EB5DA /* TopBannerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78483F0143E185EDC6ECD741 /* TopBannerModifier.swift */; };
|
||||
1B2DADC008EE211AF1DA5292 /* NotificationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30ED584467DB380E3CEFB1DB /* NotificationManagerTests.swift */; };
|
||||
1B2F9F368619FFF8C63C87CC /* BugReportScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EECE8B331CD169790EF284F /* BugReportScreenViewModelTests.swift */; };
|
||||
1B5B30839656AE2F957C6B1E /* test_pdf.pdf in Resources */ = {isa = PBXBuildFile; fileRef = BE98688578F8B0541D853695 /* test_pdf.pdf */; };
|
||||
@@ -2115,6 +2116,7 @@
|
||||
7720ACAC6155AB7F9C70B546 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nb; path = nb.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
7773CBFDBD458E0B7E270507 /* PillView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillView.swift; sourceTree = "<group>"; };
|
||||
780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
78483F0143E185EDC6ECD741 /* TopBannerModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopBannerModifier.swift; sourceTree = "<group>"; };
|
||||
787E84119E626E2F0E0BFBE8 /* ManageRoomMemberSheetModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageRoomMemberSheetModels.swift; sourceTree = "<group>"; };
|
||||
78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
78913D6E120D46138E97C107 /* NavigationSplitCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSplitCoordinatorTests.swift; sourceTree = "<group>"; };
|
||||
@@ -3599,6 +3601,7 @@
|
||||
A8558D41DD4B553A752C868A /* StackedAvatarsView.swift */,
|
||||
E10765FBC83B34A3BC4ADB23 /* TimelineScrollToBottomButton.swift */,
|
||||
16D353E10A64172D863769BF /* TombstonedAvatarImage.swift */,
|
||||
78483F0143E185EDC6ECD741 /* TopBannerModifier.swift */,
|
||||
E10DA51DBC8C7E1460DBCCBD /* UserProfileListRow.swift */,
|
||||
AD529C89924EE32CE307F36F /* VisualListItem.swift */,
|
||||
);
|
||||
@@ -8424,6 +8427,7 @@
|
||||
98EE4259A4A49BC757BA442C /* TimelineViewModel.swift in Sources */,
|
||||
F8B2F5CBCF2A0E0798E8D646 /* TimelineViewModelProtocol.swift in Sources */,
|
||||
B3D8AA9988F8A000B162DCB5 /* TombstonedAvatarImage.swift in Sources */,
|
||||
1AF4A82B4332CAD2DB3EB5DA /* TopBannerModifier.swift in Sources */,
|
||||
6E44638FDF7D4B0F80EFA7EA /* TraceLogPack.swift in Sources */,
|
||||
126CBCF5B0145FA1377C1316 /* Tracing.swift in Sources */,
|
||||
E010DDE938032D3B8E84CC35 /* TracingHook.swift in Sources */,
|
||||
|
||||
27
ElementX/Sources/Other/SwiftUI/Views/TopBannerModifier.swift
Normal file
27
ElementX/Sources/Other/SwiftUI/Views/TopBannerModifier.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
/// Overlays the given banner view at the top edge of this view, using a
|
||||
/// slide from the top edge when `isVisible` is toggled.
|
||||
func topBanner(_ banner: some View, isVisible: Bool) -> some View {
|
||||
overlay(alignment: .top) {
|
||||
ZStack {
|
||||
if isVisible {
|
||||
banner.transition(.move(edge: .top))
|
||||
} else {
|
||||
// An equal amount of space needs to be reserved in order for the transition to work.
|
||||
banner.hidden()
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: isVisible)
|
||||
.clipped()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,20 +33,15 @@ struct RoomScreen: View {
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.scrollToBottom)
|
||||
}
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.overlay(alignment: .top) {
|
||||
if !isVoiceOverEnabled {
|
||||
pinnedItemsBanner
|
||||
}
|
||||
}
|
||||
.topBanner(pinnedItemsBanner, isVisible: context.viewState.shouldShowPinnedEventsBanner && !isVoiceOverEnabled)
|
||||
// This can overlay on top of the pinnedItemsBanner
|
||||
.overlay(alignment: .top) {
|
||||
knockRequestsBanner
|
||||
}
|
||||
.topBanner(knockRequestsBanner, isVisible: context.viewState.shouldSeeKnockRequests)
|
||||
.safeAreaInset(edge: .top) {
|
||||
// When voice over is on the table view is not reversed
|
||||
// and the scroll gestures are not intercepted
|
||||
// so we render the pinned banner on top.
|
||||
if isVoiceOverEnabled {
|
||||
// When VoiceOver is enabled, the table view isn't reversed and the scroll gestures
|
||||
// don't trigger meaning the banner never hides itself and so the .overlay layout
|
||||
// above permanently obscures the top of the timeline. So whenever VoiceOver is
|
||||
// enabled we use a safe area inset to vertically stack it above the timeline.
|
||||
if context.viewState.shouldShowPinnedEventsBanner, isVoiceOverEnabled {
|
||||
pinnedItemsBanner
|
||||
}
|
||||
}
|
||||
@@ -79,41 +74,19 @@ struct RoomScreen: View {
|
||||
|
||||
@ViewBuilder
|
||||
private var pinnedItemsBanner: some View {
|
||||
// Color.clear and clipped() are required for iOS 26 transparent nav bar
|
||||
VStack(spacing: 0) {
|
||||
if context.viewState.shouldShowPinnedEventsBanner {
|
||||
PinnedItemsBannerView(state: context.viewState.pinnedEventsBannerState,
|
||||
onMainButtonTap: { context.send(viewAction: .tappedPinnedEventsBanner) },
|
||||
onViewAllButtonTap: { context.send(viewAction: .viewAllPins) })
|
||||
.transition(.move(edge: .top))
|
||||
} else {
|
||||
Color.clear
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: context.viewState.shouldShowPinnedEventsBanner)
|
||||
.clipped()
|
||||
PinnedItemsBannerView(state: context.viewState.pinnedEventsBannerState,
|
||||
onMainButtonTap: { context.send(viewAction: .tappedPinnedEventsBanner) },
|
||||
onViewAllButtonTap: { context.send(viewAction: .viewAllPins) })
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var knockRequestsBanner: some View {
|
||||
// Color.clear and clipped() are required for iOS 26 transparent nav bar
|
||||
VStack(spacing: 0) {
|
||||
if context.viewState.shouldSeeKnockRequests {
|
||||
KnockRequestsBannerView(requests: context.viewState.displayedKnockRequests,
|
||||
onDismiss: dismissKnockRequestsBanner,
|
||||
onAccept: context.viewState.canAcceptKnocks ? acceptKnockRequest : nil,
|
||||
onViewAll: onViewAllKnockRequests,
|
||||
mediaProvider: context.mediaProvider)
|
||||
.padding(.top, 16)
|
||||
.transition(.move(edge: .top))
|
||||
} else {
|
||||
Color.clear
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
}
|
||||
.animation(.elementDefault, value: context.viewState.shouldSeeKnockRequests)
|
||||
.clipped()
|
||||
KnockRequestsBannerView(requests: context.viewState.displayedKnockRequests,
|
||||
onDismiss: dismissKnockRequestsBanner,
|
||||
onAccept: context.viewState.canAcceptKnocks ? acceptKnockRequest : nil,
|
||||
onViewAll: onViewAllKnockRequests,
|
||||
mediaProvider: context.mediaProvider)
|
||||
.padding(.top, 16)
|
||||
}
|
||||
|
||||
private func dismissKnockRequestsBanner() {
|
||||
|
||||
Reference in New Issue
Block a user