mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Live Location Sharing Banner (#5370)
* Live Location Sharing Banner # Conflicts: # ElementX.xcodeproj/project.pbxproj * updated the top banner modifier and the top banner views # Conflicts: # ElementX/Resources/Localizations/en-US.lproj/Localizable.strings # ElementX/Resources/Localizations/en.lproj/Localizable.strings * improved the stopping function from the timeline item * stop live location sharing before starting a new one. * added some tests for LiveLocationManager * pr suggestions
This commit is contained in:
@@ -28,9 +28,6 @@ struct KnockRequestsBannerView: View {
|
||||
mainContent
|
||||
.padding(16)
|
||||
.background(.compound.bgCanvasDefaultLevel1, in: RoundedRectangle(cornerRadius: 12))
|
||||
.compositingGroup()
|
||||
.shadow(color: Color(red: 0.11, green: 0.11, blue: 0.13).opacity(0.1), radius: 12, x: 0, y: 4)
|
||||
.padding(.bottom, 28)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
|
||||
@@ -202,6 +199,14 @@ struct KnockRequestsBannerView_Previews: PreviewProvider, TestablePreview {
|
||||
]
|
||||
|
||||
static var previews: some View {
|
||||
allPreviews
|
||||
.padding()
|
||||
.background(.gray)
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
static var allPreviews: some View {
|
||||
KnockRequestsBannerView(requests: singleRequest) { } onAccept: { _ in } onViewAll: { }
|
||||
.previewDisplayName("Single Request")
|
||||
// swiftlint:disable:next trailing_closure
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// 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 Compound
|
||||
import SwiftUI
|
||||
|
||||
struct LiveLocationSharingBannerView: View {
|
||||
var onStop: () -> Void
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 9) {
|
||||
CompoundIcon(\.locationPinSolid, size: .medium, relativeTo: .compound.bodyMDSemibold)
|
||||
.foregroundColor(Color.compound.iconSuccessPrimary)
|
||||
.accessibilityHidden(true)
|
||||
Text(L10n.screenRoomLiveLocationBanner)
|
||||
.font(.compound.bodyMDSemibold)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
Spacer()
|
||||
Button(L10n.actionStop, role: .destructive, action: onStop)
|
||||
.buttonStyle(.compound(.primary, size: .small))
|
||||
}
|
||||
.padding(.vertical, 16)
|
||||
.padding(.horizontal, 15)
|
||||
.background(Color.compound.bgCanvasDefault)
|
||||
.overlay(alignment: .top) { Color.compound.separatorPrimary.frame(height: 1) }
|
||||
.overlay(alignment: .bottom) { Color.compound.separatorPrimary.frame(height: 1) }
|
||||
}
|
||||
}
|
||||
|
||||
struct LiveLocationSharingBannerView_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
LiveLocationSharingBannerView { }
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -23,9 +23,6 @@ struct PinnedItemsBannerView: View {
|
||||
.padding(.vertical, 16)
|
||||
.padding(.leading, 16)
|
||||
.background(Color.compound.bgCanvasDefault)
|
||||
.shadow(color: Color(red: 0.11, green: 0.11, blue: 0.13).opacity(0.1), radius: 12, x: 0, y: 4)
|
||||
// To include the shadow in the size
|
||||
.padding(.bottom, 28)
|
||||
}
|
||||
|
||||
private var mainButton: some View {
|
||||
@@ -111,5 +108,8 @@ struct PinnedItemsBannerView_Previews: PreviewProvider, TestablePreview {
|
||||
onMainButtonTap: { },
|
||||
onViewAllButtonTap: { })
|
||||
}
|
||||
.padding()
|
||||
.background(.gray)
|
||||
.previewLayout(.sizeThatFits)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,17 +35,27 @@ struct RoomScreen: View {
|
||||
}
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.topBanners([
|
||||
TopBannerItem(pinnedItemsBanner, isVisible: context.viewState.shouldShowPinnedEventsBanner && !isVoiceOverEnabled),
|
||||
// This can overlay on top of the pinnedItemsBanner
|
||||
TopBannerItem(knockRequestsBanner, isVisible: context.viewState.shouldSeeKnockRequests)
|
||||
TopBannerLayer(verticalBanners: [
|
||||
TopBannerItem(pinnedItemsBanner, isVisible: context.viewState.shouldShowPinnedEventsBanner && !isVoiceOverEnabled),
|
||||
TopBannerItem(liveLocationBanner, isVisible: context.viewState.isSharingLiveLocation && !isVoiceOverEnabled)
|
||||
]),
|
||||
// This can overlay on top of the stacked banners
|
||||
TopBannerLayer(knockRequestsBanner, isVisible: context.viewState.shouldSeeKnockRequests)
|
||||
], footer: dateBadge)
|
||||
.safeAreaInset(edge: .top) {
|
||||
// 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
|
||||
if context.viewState.shouldShowPinnedEventsBanner || context.viewState.isSharingLiveLocation, isVoiceOverEnabled {
|
||||
VStack(spacing: 0) {
|
||||
if context.viewState.shouldShowPinnedEventsBanner {
|
||||
pinnedItemsBanner
|
||||
}
|
||||
if context.viewState.isSharingLiveLocation {
|
||||
liveLocationBanner
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||
@@ -76,6 +86,12 @@ struct RoomScreen: View {
|
||||
.sentryTrace("\(Self.self)")
|
||||
}
|
||||
|
||||
private var liveLocationBanner: some View {
|
||||
LiveLocationSharingBannerView {
|
||||
context.send(viewAction: .tappedStopLiveLocation)
|
||||
}
|
||||
}
|
||||
|
||||
private var pinnedItemsBanner: some View {
|
||||
PinnedItemsBannerView(state: context.viewState.pinnedEventsBannerState,
|
||||
onMainButtonTap: { context.send(viewAction: .tappedPinnedEventsBanner) },
|
||||
|
||||
Reference in New Issue
Block a user