mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Fix a bug where the timeline disappeared when VoiceOver was enabled. (#4701)
This commit is contained in:
@@ -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