mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 12:07:55 +00:00
improved location sheets presentations and addressed some pr suggestions
This commit is contained in:
@@ -2516,7 +2516,7 @@ internal enum L10n {
|
||||
internal static var screenRoomAttachmentSourceFiles: String { return L10n.tr("Localizable", "screen_room_attachment_source_files") }
|
||||
/// Photo & Video Library
|
||||
internal static var screenRoomAttachmentSourceGallery: String { return L10n.tr("Localizable", "screen_room_attachment_source_gallery") }
|
||||
/// Location
|
||||
/// Share location
|
||||
internal static var screenRoomAttachmentSourceLocation: String { return L10n.tr("Localizable", "screen_room_attachment_source_location") }
|
||||
/// Poll
|
||||
internal static var screenRoomAttachmentSourcePoll: String { return L10n.tr("Localizable", "screen_room_attachment_source_poll") }
|
||||
@@ -3171,8 +3171,10 @@ internal enum L10n {
|
||||
internal static var screenShareOpenGoogleMaps: String { return L10n.tr("Localizable", "screen_share_open_google_maps") }
|
||||
/// Open in OpenStreetMap
|
||||
internal static var screenShareOpenOsmMaps: String { return L10n.tr("Localizable", "screen_share_open_osm_maps") }
|
||||
/// Share pinned location
|
||||
/// Share selected location
|
||||
internal static var screenShareThisLocationAction: String { return L10n.tr("Localizable", "screen_share_this_location_action") }
|
||||
/// Sharing options
|
||||
internal static var screenSharingLocationOptionSheetTitle: String { return L10n.tr("Localizable", "screen_sharing_location_option_sheet_title") }
|
||||
/// You’ve changed your password on another session
|
||||
internal static var screenSignedOutReason1: String { return L10n.tr("Localizable", "screen_signed_out_reason_1") }
|
||||
/// You have deleted the session from another session
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Copyright 2026 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 Foundation
|
||||
|
||||
struct StaticLocationData: Hashable {
|
||||
let sender: TimelineItemSender
|
||||
let geoURI: GeoURI
|
||||
let kind: StaticLocationKind
|
||||
let timestamp: Date
|
||||
}
|
||||
@@ -72,8 +72,9 @@ struct LocationSharingScreenViewState: BindableState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the user's location has not yet been determined, while location permissions are given or not yet set
|
||||
/// Does not work as intended on simulator.
|
||||
var isLocationLoading: Bool {
|
||||
// May not work as intended on simulator
|
||||
!bindings.hasLoadedUserLocation && bindings.isLocationAuthorized != false
|
||||
}
|
||||
|
||||
@@ -92,7 +93,7 @@ struct LocationSharingScreenViewState: BindableState {
|
||||
|
||||
var userProfile: UserProfileProxy?
|
||||
|
||||
var staticLocationMarkerUserProfile: UserProfileProxy? {
|
||||
var locationMarkerUserProfile: UserProfileProxy? {
|
||||
switch interactionMode {
|
||||
case .picker:
|
||||
isSharingUserLocation ? userProfile : nil
|
||||
@@ -159,10 +160,3 @@ extension AlertInfo where T == LocationSharingViewError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StaticLocationData: Hashable {
|
||||
let sender: TimelineItemSender
|
||||
let geoURI: GeoURI
|
||||
let kind: StaticLocationKind
|
||||
let timestamp: Date
|
||||
}
|
||||
|
||||
@@ -132,7 +132,9 @@ extension LocationSharingScreenViewModel {
|
||||
case staticPinLocation
|
||||
}
|
||||
|
||||
static func mock(type: MockType, senderID: String = "@dan:matrix.org") -> LocationSharingScreenViewModel {
|
||||
static func mock(type: MockType,
|
||||
senderID: String = "@dan:matrix.org",
|
||||
liveLocationSharingEnabled: Bool = true) -> LocationSharingScreenViewModel {
|
||||
let interactionMode: LocationSharingInteractionMode = switch type {
|
||||
case .picker:
|
||||
.picker
|
||||
@@ -152,7 +154,7 @@ extension LocationSharingScreenViewModel {
|
||||
|
||||
return LocationSharingScreenViewModel(interactionMode: interactionMode,
|
||||
mapURLBuilder: ServiceLocator.shared.settings.mapTilerConfiguration,
|
||||
liveLocationSharingEnabled: true,
|
||||
liveLocationSharingEnabled: liveLocationSharingEnabled,
|
||||
roomProxy: JoinedRoomProxyMock(.init()),
|
||||
timelineController: MockTimelineController(),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
|
||||
@@ -12,8 +12,20 @@ struct LocationPickerSheet: View {
|
||||
@Bindable var context: LocationSharingScreenViewModel.Context
|
||||
@State private var height: CGFloat = .zero
|
||||
|
||||
/// Fixes an iOS 26 sheet issue
|
||||
/// if the content doesn't meet a certain size
|
||||
/// additional insets are added.
|
||||
private var additionalHeight: CGFloat {
|
||||
context.viewState.showLiveLocationSharingButton ? 0 : 28
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
VStack(spacing: 0) {
|
||||
Text(L10n.screenSharingLocationOptionSheetTitle)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
.font(.compound.bodyLGSemibold)
|
||||
.padding(.top, 29)
|
||||
.padding(.bottom, 25)
|
||||
Button {
|
||||
context.send(viewAction: .selectLocation)
|
||||
} label: {
|
||||
@@ -35,15 +47,12 @@ struct LocationPickerSheet: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
.padding(.top, 38)
|
||||
.readHeight($height)
|
||||
.interactiveDismissDisabled()
|
||||
.presentationBackground(.compound.bgCanvasDefault)
|
||||
.presentationBackgroundInteraction(.enabled)
|
||||
.presentationDragIndicator(.hidden)
|
||||
.presentationDetents([.height(height)])
|
||||
.presentationDetents([.height(height + additionalHeight)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +68,8 @@ private struct LocationPickerLabel: View {
|
||||
.padding(.vertical, 14)
|
||||
.rowDivider(alignment: .top)
|
||||
.padding(.trailing, 16)
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
} icon: {
|
||||
CompoundIcon(icon)
|
||||
.foregroundStyle(iconColor)
|
||||
|
||||
@@ -56,7 +56,7 @@ struct LocationSharingScreen: View {
|
||||
.ignoresSafeArea(.all, edges: mapSafeAreaEdges)
|
||||
|
||||
if context.viewState.isLocationPickerMode {
|
||||
LocationMarkerView(userProfile: context.viewState.staticLocationMarkerUserProfile, mediaProvider: context.mediaProvider)
|
||||
LocationMarkerView(userProfile: context.viewState.locationMarkerUserProfile, mediaProvider: context.mediaProvider)
|
||||
}
|
||||
}
|
||||
.overlay(alignment: .topTrailing) {
|
||||
@@ -76,11 +76,11 @@ struct LocationSharingScreen: View {
|
||||
private var mapOptions: MapLibreMapView.Options {
|
||||
var annotations: [String: LocationAnnotation] = [:]
|
||||
if !context.viewState.isLocationPickerMode {
|
||||
let id = context.viewState.staticLocationMarkerUserProfile?.userID ?? UUID().uuidString
|
||||
let id = context.viewState.locationMarkerUserProfile?.userID ?? UUID().uuidString
|
||||
let annotation = LocationAnnotation(id: id,
|
||||
coordinate: context.viewState.initialMapCenter,
|
||||
anchorPoint: .bottomCenter) {
|
||||
LocationMarkerView(userProfile: context.viewState.staticLocationMarkerUserProfile, mediaProvider: context.mediaProvider)
|
||||
LocationMarkerView(userProfile: context.viewState.locationMarkerUserProfile, mediaProvider: context.mediaProvider)
|
||||
}
|
||||
annotations[id] = annotation
|
||||
}
|
||||
@@ -129,10 +129,10 @@ struct LocationSharingScreen: View {
|
||||
@ViewBuilder
|
||||
private var shareSheet: some View {
|
||||
let location = context.viewState.initialMapCenter
|
||||
let senderName = context.viewState.staticLocationMarkerUserProfile?.displayName ?? context.viewState.staticLocationMarkerUserProfile?.userID
|
||||
let senderName = context.viewState.locationMarkerUserProfile?.displayName ?? context.viewState.locationMarkerUserProfile?.userID
|
||||
AppActivityView(activityItems: [ShareToMapsAppActivity.MapsAppType.apple.activityURL(for: location, senderName: senderName)],
|
||||
applicationActivities: ShareToMapsAppActivity.MapsAppType.allCases.map { ShareToMapsAppActivity(type: $0, location: location, senderName: senderName) })
|
||||
.edgesIgnoringSafeArea(.bottom)
|
||||
.ignoresSafeArea(edges: .bottom)
|
||||
.presentationDetents([.medium, .large])
|
||||
.presentationCompactAdaptation(shareSheetCompactPresentation)
|
||||
.presentationDragIndicator(.hidden)
|
||||
@@ -152,6 +152,8 @@ struct LocationSharingScreen: View {
|
||||
struct LocationSharingScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = LocationSharingScreenViewModel.mock(type: .staticSenderLocation)
|
||||
|
||||
static let withoutLiveSharingViewModel = LocationSharingScreenViewModel.mock(type: .picker, liveLocationSharingEnabled: false)
|
||||
|
||||
static let pinViewModel = LocationSharingScreenViewModel.mock(type: .staticPinLocation)
|
||||
|
||||
static let pickerViewModel = LocationSharingScreenViewModel.mock(type: .picker)
|
||||
@@ -162,6 +164,11 @@ struct LocationSharingScreen_Previews: PreviewProvider, TestablePreview {
|
||||
}
|
||||
.previewDisplayName("Picker")
|
||||
|
||||
ElementNavigationStack {
|
||||
LocationSharingScreen(context: withoutLiveSharingViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Picker without live location sharing")
|
||||
|
||||
ElementNavigationStack {
|
||||
LocationSharingScreen(context: viewModel.context)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,11 @@ struct StaticLocationSheet: View {
|
||||
@Bindable var context: LocationSharingScreenViewModel.Context
|
||||
@State private var height = CGFloat.zero
|
||||
|
||||
/// Fixes an iOS 26 sheet issue
|
||||
/// if the content doesn't meet a certain size
|
||||
/// additional insets are added.
|
||||
private let additionalHeight: CGFloat = 14
|
||||
|
||||
var body: some View {
|
||||
mainContent
|
||||
.readHeight($height)
|
||||
@@ -19,19 +24,18 @@ struct StaticLocationSheet: View {
|
||||
.presentationBackground(.compound.bgCanvasDefault)
|
||||
.presentationBackgroundInteraction(.enabled)
|
||||
.presentationDragIndicator(.hidden)
|
||||
.presentationDetents([.height(height)])
|
||||
.presentationDetents([.height(height + additionalHeight)])
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var mainContent: some View {
|
||||
if case let .viewStatic(location) = context.viewState.interactionMode,
|
||||
let userProfile = context.viewState.userProfile {
|
||||
VStack(spacing: 0) {
|
||||
Text(L10n.screenStaticLocationSheetTitle)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
.font(.compound.bodyLGSemibold)
|
||||
.padding(.top, 29)
|
||||
.padding(.bottom, 25)
|
||||
VStack(spacing: 0) {
|
||||
Text(L10n.screenStaticLocationSheetTitle)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
.font(.compound.bodyLGSemibold)
|
||||
.padding(.bottom, 25)
|
||||
.padding(.top, 29)
|
||||
if case let .viewStatic(location) = context.viewState.interactionMode,
|
||||
let userProfile = context.viewState.userProfile {
|
||||
Button {
|
||||
context.showShareSheet = true
|
||||
} label: {
|
||||
|
||||
Reference in New Issue
Block a user