diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 12491212f..dd1f0bcea 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -705,7 +705,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { stateMachine.tryEvent(.presentEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis), userInfo: EventUserInfo(animated: animated, timelineController: timelineController)) case .presentLocationPicker: - stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker), + stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker(shouldShowLiveLocationOption: true)), userInfo: EventUserInfo(animated: animated, timelineController: timelineController)) case .presentPollForm(let mode): stateMachine.tryEvent(.presentPollForm(mode: mode), @@ -814,7 +814,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { stateMachine.tryEvent(.presentMediaUploadPreview(mediaURLs: mediaURLs), userInfo: EventUserInfo(animated: animated, timelineController: timelineController)) case .presentLocationPicker: - stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker), + stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker(shouldShowLiveLocationOption: false)), userInfo: EventUserInfo(animated: animated, timelineController: timelineController)) case .presentLiveLocationViewer(let sender, let initialLiveLocationShare): stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewLive(sender: sender, initialLiveLocationShare: initialLiveLocationShare)), @@ -1167,7 +1167,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { timelineController: TimelineControllerProtocol, animated: Bool) { let stackCoordinator = NavigationStackCoordinator() - + let params = LocationSharingScreenCoordinatorParameters(interactionMode: interactionMode, mapURLBuilder: flowParameters.appSettings.mapTilerConfiguration, roomProxy: roomProxy, diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift index d20133c63..016ae724c 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift @@ -224,7 +224,7 @@ extension RoomFlowCoordinator { case (.mediaEventsTimeline, .presentMessageForwarding(forwardingItem: let forwardingItem)): return .messageForwarding(forwardingItem: forwardingItem, previousState: fromState) - case (.room, .presentMapNavigator(_)): + case (.room, .presentMapNavigator): return .mapNavigator(previousState: fromState) case (.room, .presentPollForm): @@ -271,7 +271,7 @@ extension RoomFlowCoordinator { case (.thread, .presentMessageForwarding(let forwardingItem)): return .messageForwarding(forwardingItem: forwardingItem, previousState: fromState) - case (.thread, .presentMapNavigator(_)): + case (.thread, .presentMapNavigator): return .mapNavigator(previousState: fromState) case (.thread, .presentPollForm): diff --git a/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenModels.swift b/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenModels.swift index bf1aafa80..3a4199c30 100644 --- a/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenModels.swift +++ b/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenModels.swift @@ -25,9 +25,21 @@ enum LocationSharingScreenViewModelAction { } enum LocationSharingInteractionMode: Hashable { - case picker + case picker(shouldShowLiveLocationOption: Bool) case viewStatic(StaticLocationData) case viewLive(sender: TimelineItemSender?, initialLiveLocationShare: LiveLocationShare?) + + var isPicker: Bool { + if case .picker = self { return true } + return false + } + + var shouldShowLiveLocationOption: Bool { + if case .picker(let shouldShowLiveLocationOption) = self { + return shouldShowLiveLocationOption + } + return false + } } struct LocationSharingScreenViewState: BindableState { diff --git a/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenViewModel.swift b/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenViewModel.swift index 2c9d71d0e..6e8eec4e9 100644 --- a/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenViewModel.swift +++ b/ElementX/Sources/Screens/LocationSharing/LocationSharingScreenViewModel.swift @@ -46,7 +46,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati self.analytics = analytics self.userIndicatorController = userIndicatorController self.notificationCenter = notificationCenter - + super.init(initialViewState: .init(interactionMode: interactionMode, mapURLBuilder: mapURLBuilder, ownUserID: roomProxy.ownUserID), @@ -332,17 +332,20 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati extension LocationSharingScreenViewModel { enum MockType { case picker + case pickerWithoutLiveLocationOption case staticSenderLocation case staticPinLocation case viewLive case viewLiveEmpty } - + static func mock(type: MockType, senderID: String = "@dan:matrix.org") -> LocationSharingScreenViewModel { let interactionMode: LocationSharingInteractionMode = switch type { case .picker: - .picker + .picker(shouldShowLiveLocationOption: true) + case .pickerWithoutLiveLocationOption: + .picker(shouldShowLiveLocationOption: false) case .staticPinLocation: .viewStatic(.init(sender: .init(id: senderID), geoURI: .init(latitude: 41.9027835, diff --git a/ElementX/Sources/Screens/LocationSharing/View/LocationPickerSheet.swift b/ElementX/Sources/Screens/LocationSharing/View/LocationPickerSheet.swift index cc054a532..bbf5e7b8d 100644 --- a/ElementX/Sources/Screens/LocationSharing/View/LocationPickerSheet.swift +++ b/ElementX/Sources/Screens/LocationSharing/View/LocationPickerSheet.swift @@ -11,7 +11,14 @@ import SwiftUI 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.interactionMode.shouldShowLiveLocationOption ? 0 : 28 + } + var body: some View { VStack(spacing: 0) { Text(L10n.screenSharingLocationOptionSheetTitle) @@ -34,12 +41,14 @@ struct LocationPickerSheet: View { } } - Button { - context.send(viewAction: .startLiveLocation) - } label: { - LocationPickerLabel(text: L10n.actionShareLiveLocation, - icon: \.locationPinSolid, - iconColor: .compound.iconAccentPrimary) + if context.viewState.interactionMode.shouldShowLiveLocationOption { + Button { + context.send(viewAction: .startLiveLocation) + } label: { + LocationPickerLabel(text: L10n.actionShareLiveLocation, + icon: \.locationPinSolid, + iconColor: .compound.iconAccentPrimary) + } } } .readHeight($height) @@ -47,7 +56,7 @@ struct LocationPickerSheet: View { .presentationBackground(.compound.bgCanvasDefault) .presentationBackgroundInteraction(.enabled) .presentationDragIndicator(.hidden) - .presentationDetents([.height(height)]) + .presentationDetents([.height(height + additionalHeight)]) } } diff --git a/ElementX/Sources/Screens/LocationSharing/View/LocationSharingScreen.swift b/ElementX/Sources/Screens/LocationSharing/View/LocationSharingScreen.swift index 826d21f61..c9a7a7b5e 100644 --- a/ElementX/Sources/Screens/LocationSharing/View/LocationSharingScreen.swift +++ b/ElementX/Sources/Screens/LocationSharing/View/LocationSharingScreen.swift @@ -40,7 +40,7 @@ struct LocationSharingScreen: View { private var mainContent: some View { mapView .ignoresSafeArea(edges: .bottom) - .track(screen: context.viewState.interactionMode == .picker ? .LocationSend : .LocationView) + .track(screen: context.viewState.interactionMode.isPicker ? .LocationSend : .LocationView) .navigationTitle(L10n.screenViewLocationTitle) .navigationBarTitleDisplayMode(.inline) .toolbar { toolbar } @@ -71,7 +71,7 @@ struct LocationSharingScreen: View { } private var mapSafeAreaEdges: Edge.Set { - context.viewState.interactionMode == .picker ? .horizontal : [.horizontal, .bottom] + context.viewState.interactionMode.isPicker ? .horizontal : [.horizontal, .bottom] } @ToolbarContentBuilder @@ -126,19 +126,26 @@ struct LocationSharingScreen: View { struct LocationSharingScreen_Previews: PreviewProvider, TestablePreview { static let viewModel = LocationSharingScreenViewModel.mock(type: .staticSenderLocation) - + + static let withoutLiveSharingViewModel = LocationSharingScreenViewModel.mock(type: .pickerWithoutLiveLocationOption) + static let pinViewModel = LocationSharingScreenViewModel.mock(type: .staticPinLocation) - + static let pickerViewModel = LocationSharingScreenViewModel.mock(type: .picker) - + static let liveLocationViewModel = LocationSharingScreenViewModel.mock(type: .viewLive) - + static var previews: some View { ElementNavigationStack { LocationSharingScreen(context: pickerViewModel.context) } .previewDisplayName("Picker") - + + ElementNavigationStack { + LocationSharingScreen(context: withoutLiveSharingViewModel.context) + } + .previewDisplayName("Picker without live location sharing") + ElementNavigationStack { LocationSharingScreen(context: viewModel.context) } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift index c0eca6a4f..dd33acadb 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift @@ -440,7 +440,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol { timestamp: eventItemProxy.timestamp, isOutgoing: isOutgoing, isEditable: eventItemProxy.isEditable, - canBeRepliedTo: eventItemProxy.canBeRepliedTo, + // Workaround until https://github.com/matrix-org/matrix-rust-sdk/pull/6563 is merged. + canBeRepliedTo: false, sender: eventItemProxy.sender, content: .init(from: liveLocationContent, timestamp: eventItemProxy.timestamp), properties: .init(replyDetails: buildTimelineItemReplyDetails(messageLikeContent.inReplyTo), diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPad-en-GB.png new file mode 100644 index 000000000..3fa30dbfb --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPad-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64737509400fe27cdb017b8f66cffe8c0807f958827cff84a9ce5484db0ca3fc +size 80109 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPad-pseudo.png new file mode 100644 index 000000000..3fa30dbfb --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPad-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64737509400fe27cdb017b8f66cffe8c0807f958827cff84a9ce5484db0ca3fc +size 80109 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPhone-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPhone-en-GB.png new file mode 100644 index 000000000..95519c8ea --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPhone-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08e72ae0c7d34108690371b29952267b1ac06bb62f203515f3c5e4143efd2ca8 +size 38511 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPhone-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPhone-pseudo.png new file mode 100644 index 000000000..95519c8ea --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/locationSharingScreen.Picker-without-live-location-sharing-iPhone-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08e72ae0c7d34108690371b29952267b1ac06bb62f203515f3c5e4143efd2ca8 +size 38511 diff --git a/UnitTests/Sources/LocationSharingScreenViewModelTests.swift b/UnitTests/Sources/LocationSharingScreenViewModelTests.swift index 5d99ac6f2..f8cc6f46c 100644 --- a/UnitTests/Sources/LocationSharingScreenViewModelTests.swift +++ b/UnitTests/Sources/LocationSharingScreenViewModelTests.swift @@ -402,7 +402,7 @@ struct LocationSharingScreenViewModelTests { analytics: ServiceLocator.shared.analytics, userIndicatorController: UserIndicatorControllerMock(), mediaProvider: MediaProviderMock(configuration: .init())) - + // Initially no annotations and no map center since sender and share are both nil. #expect(context.viewState.annotations.isEmpty) #expect(context.mapCenterLocation == nil) @@ -428,7 +428,7 @@ struct LocationSharingScreenViewModelTests { private mutating func setupViewModel(liveLocationManagerConfiguration: LiveLocationManagerMock.Configuration = .init(), members: [RoomMemberProxyMock] = .allMembersAsAdmin) { timelineProxy = TimelineProxyMock(.init()) - viewModel = LocationSharingScreenViewModel(interactionMode: .picker, + viewModel = LocationSharingScreenViewModel(interactionMode: .picker(shouldShowLiveLocationOption: true), mapURLBuilder: ServiceLocator.shared.settings.mapTilerConfiguration, roomProxy: JoinedRoomProxyMock(.init(members: members)), timelineController: MockTimelineController(timelineProxy: timelineProxy), @@ -438,11 +438,11 @@ struct LocationSharingScreenViewModelTests { mediaProvider: MediaProviderMock(configuration: .init())) viewModel.state.bindings.isLocationAuthorized = true } - + private mutating func setupViewModel(liveLocationManagerMock: LiveLocationManagerMock, members: [RoomMemberProxyMock] = .allMembersAsAdmin) { timelineProxy = TimelineProxyMock(.init()) - viewModel = LocationSharingScreenViewModel(interactionMode: .picker, + viewModel = LocationSharingScreenViewModel(interactionMode: .picker(shouldShowLiveLocationOption: true), mapURLBuilder: ServiceLocator.shared.settings.mapTilerConfiguration, roomProxy: JoinedRoomProxyMock(.init(members: members)), timelineController: MockTimelineController(timelineProxy: timelineProxy),