improved location sheets presentations and addressed some pr suggestions
This commit is contained in:
@@ -979,6 +979,7 @@
|
||||
A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; };
|
||||
A7455F6B2F5AF7DD00E8D8BB /* LocationPickerSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7455F6A2F5AF7D400E8D8BB /* LocationPickerSheet.swift */; };
|
||||
A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
||||
A7D5FE842F5F2EC000386C15 /* StaticLocationData.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7D5FE832F5F2EBB00386C15 /* StaticLocationData.swift */; };
|
||||
A7DB75E090542331F6668A23 /* CreateRoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF19027E7FFA5E63D148873A /* CreateRoomScreenViewModel.swift */; };
|
||||
A808DC3F72D15C6C5A52317E /* TimelineItemDebugView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCDA016D05107DED3B9495CB /* TimelineItemDebugView.swift */; };
|
||||
A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; };
|
||||
@@ -2463,6 +2464,7 @@
|
||||
A7A1B80FE6E3BA72F9C748AD /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = "<group>"; };
|
||||
A7D452AF7B5F7E3A0A7DB54C /* SessionVerificationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
A7D5FE832F5F2EBB00386C15 /* StaticLocationData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationData.swift; sourceTree = "<group>"; };
|
||||
A7E37072597F67C4DD8CC2DB /* ComposerDraftServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerDraftServiceProtocol.swift; sourceTree = "<group>"; };
|
||||
A84D413BF49F0E980F010A6B /* LogViewerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewerScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
A8558D41DD4B553A752C868A /* StackedAvatarsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackedAvatarsView.swift; sourceTree = "<group>"; };
|
||||
@@ -6065,6 +6067,7 @@
|
||||
C17C3586C93F3A314C1CC318 /* MapLibre */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A7D5FE832F5F2EBB00386C15 /* StaticLocationData.swift */,
|
||||
AAD8234D0E9C9B12BF9F240B /* LocationAnnotation.swift */,
|
||||
622D09D4ECE759189009AEAF /* MapLibreMapView.swift */,
|
||||
B81B6170DB690013CEB646F4 /* MapLibreModels.swift */,
|
||||
@@ -8674,6 +8677,7 @@
|
||||
DF004A5B2EABBD0574D06A04 /* SplashScreenCoordinator.swift in Sources */,
|
||||
E1C67E5D9E22135A8FEBBD60 /* StackedAvatarsView.swift in Sources */,
|
||||
9FC79DA30AE0E1502DAEBD51 /* StartChatFlowCoordinator.swift in Sources */,
|
||||
A7D5FE842F5F2EC000386C15 /* StaticLocationData.swift in Sources */,
|
||||
3DAF325D8AE461F7CDB282BD /* StartChatScreen.swift in Sources */,
|
||||
6CD61FAF03E8986523C2ABB8 /* StartChatScreenCoordinator.swift in Sources */,
|
||||
C051475DFF4C8EBDDF4DC8E4 /* StartChatScreenModels.swift in Sources */,
|
||||
|
||||
@@ -748,6 +748,7 @@
|
||||
"screen_security_and_privacy_room_visibility_section_footer" = "Addresses are a way to find and access rooms and spaces. This also ensures you can easily share them with others.";
|
||||
"screen_security_and_privacy_room_visibility_section_header" = "Visibility";
|
||||
"screen_security_and_privacy_title" = "Security & privacy";
|
||||
"screen_sharing_location_option_sheet_title" = "Sharing options";
|
||||
"screen_space_add_room_action" = "Room";
|
||||
"screen_space_empty_state_title" = "Add your first room";
|
||||
"screen_space_menu_action_members" = "View members";
|
||||
@@ -1095,7 +1096,7 @@
|
||||
"screen_room_attachment_source_camera_video" = "Record video";
|
||||
"screen_room_attachment_source_files" = "Attachment";
|
||||
"screen_room_attachment_source_gallery" = "Photo & Video Library";
|
||||
"screen_room_attachment_source_location" = "Location";
|
||||
"screen_room_attachment_source_location" = "Share location";
|
||||
"screen_room_attachment_source_poll" = "Poll";
|
||||
"screen_room_attachment_text_formatting" = "Text Formatting";
|
||||
"screen_room_change_permissions_administrators" = "Admin";
|
||||
@@ -1275,7 +1276,7 @@
|
||||
"screen_share_open_apple_maps" = "Open in Apple Maps";
|
||||
"screen_share_open_google_maps" = "Open in Google Maps";
|
||||
"screen_share_open_osm_maps" = "Open in OpenStreetMap";
|
||||
"screen_share_this_location_action" = "Share pinned location";
|
||||
"screen_share_this_location_action" = "Share selected location";
|
||||
"screen_signed_out_reason_1" = "You’ve changed your password on another session";
|
||||
"screen_signed_out_reason_2" = "You have deleted the session from another session";
|
||||
"screen_signed_out_reason_3" = "Your server’s administrator has invalidated your access";
|
||||
|
||||
@@ -748,6 +748,7 @@
|
||||
"screen_security_and_privacy_room_visibility_section_footer" = "Addresses are a way to find and access rooms and spaces. This also ensures you can easily share them with others.";
|
||||
"screen_security_and_privacy_room_visibility_section_header" = "Visibility";
|
||||
"screen_security_and_privacy_title" = "Security & privacy";
|
||||
"screen_sharing_location_option_sheet_title" = "Sharing options";
|
||||
"screen_space_add_room_action" = "Room";
|
||||
"screen_space_empty_state_title" = "Add your first room";
|
||||
"screen_space_menu_action_members" = "View members";
|
||||
@@ -1095,7 +1096,7 @@
|
||||
"screen_room_attachment_source_camera_video" = "Record video";
|
||||
"screen_room_attachment_source_files" = "Attachment";
|
||||
"screen_room_attachment_source_gallery" = "Photo & Video Library";
|
||||
"screen_room_attachment_source_location" = "Location";
|
||||
"screen_room_attachment_source_location" = "Share location";
|
||||
"screen_room_attachment_source_poll" = "Poll";
|
||||
"screen_room_attachment_text_formatting" = "Text Formatting";
|
||||
"screen_room_change_permissions_administrators" = "Admin";
|
||||
@@ -1275,7 +1276,7 @@
|
||||
"screen_share_open_apple_maps" = "Open in Apple Maps";
|
||||
"screen_share_open_google_maps" = "Open in Google Maps";
|
||||
"screen_share_open_osm_maps" = "Open in OpenStreetMap";
|
||||
"screen_share_this_location_action" = "Share pinned location";
|
||||
"screen_share_this_location_action" = "Share selected location";
|
||||
"screen_signed_out_reason_1" = "You’ve changed your password on another session";
|
||||
"screen_signed_out_reason_2" = "You have deleted the session from another session";
|
||||
"screen_signed_out_reason_3" = "Your server’s administrator has invalidated your access";
|
||||
|
||||
@@ -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
|
||||
|
||||
15
ElementX/Sources/Other/MapLibre/StaticLocationData.swift
Normal file
15
ElementX/Sources/Other/MapLibre/StaticLocationData.swift
Normal file
@@ -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: {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user