diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 19b87a4a5..3ac1232ba 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -8736,7 +8736,7 @@ repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift"; requirement = { kind = exactVersion; - version = 25.06.11; + version = 25.06.12; }; }; 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = { diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 728521ba0..6e9278784 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -158,8 +158,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/element-hq/matrix-rust-components-swift", "state" : { - "revision" : "0d5ded2509e9d409b33e4f33a45a8ee027b94828", - "version" : "25.6.11" + "revision" : "6450696917e54b4ab62cad9275d673e43d0e865c", + "version" : "25.6.12" } }, { diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 0d96707fe..544f5b481 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -310,8 +310,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { case (_, .presentMessageForwarding(let forwardingItem), .messageForwarding): presentMessageForwarding(with: forwardingItem) - case (_, .presentMapNavigator(let mode), .mapNavigator): - presentMapNavigator(interactionMode: mode) + case (_, .presentMapNavigator(let mode, let threadRootEventID), .mapNavigator): + presentMapNavigator(interactionMode: mode, threadRootEventID: threadRootEventID) case (_, .presentPollForm(let mode), .pollForm): presentPollForm(mode: mode) @@ -528,19 +528,25 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { case .presentRoomDetails: stateMachine.tryEvent(.presentRoomDetails) case .presentReportContent(let itemID, let senderID): - stateMachine.tryEvent(.presentReportContent(itemID: itemID, senderID: senderID)) + stateMachine.tryEvent(.presentReportContent(itemID: itemID, + senderID: senderID)) case .presentMediaUploadPicker(let source): - stateMachine.tryEvent(.presentMediaUploadPicker(source: source, threadRootEventID: nil)) + stateMachine.tryEvent(.presentMediaUploadPicker(source: source, + threadRootEventID: nil)) case .presentMediaUploadPreviewScreen(let url): - stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: url, threadRootEventID: nil)) + stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: url, + threadRootEventID: nil)) case .presentEmojiPicker(let itemID, let selectedEmojis): - stateMachine.tryEvent(.presentEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis)) + stateMachine.tryEvent(.presentEmojiPicker(itemID: itemID, + selectedEmojis: selectedEmojis)) case .presentLocationPicker: - stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker)) + stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker, + threadRootEventID: nil)) case .presentPollForm(let mode): stateMachine.tryEvent(.presentPollForm(mode: mode)) case .presentLocationViewer(_, let geoURI, let description): - stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, description: description))) + stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, description: description), + threadRootEventID: nil)) case .presentRoomMemberDetails(userID: let userID): stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID)) case .presentMessageForwarding(let forwardingItem): @@ -550,13 +556,16 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { case .presentPinnedEventsTimeline: stateMachine.tryEvent(.presentPinnedEventsTimeline) case .presentResolveSendFailure(failure: let failure, sendHandle: let sendHandle): - stateMachine.tryEvent(.presentResolveSendFailure(failure: failure, sendHandle: sendHandle)) + stateMachine.tryEvent(.presentResolveSendFailure(failure: failure, + sendHandle: sendHandle)) case .presentKnockRequestsList: stateMachine.tryEvent(.presentKnockRequestsListScreen) case .presentThread(let itemID): stateMachine.tryEvent(.presentThread(itemID: itemID)) case .presentRoom(roomID: let roomID): - stateMachine.tryEvent(.startChildFlow(roomID: roomID, via: [], entryPoint: .room)) + stateMachine.tryEvent(.startChildFlow(roomID: roomID, + via: [], + entryPoint: .room)) } } .store(in: &cancellables) @@ -613,20 +622,25 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { case .presentMediaUploadPreviewScreen(let url, let threadRootEventID): stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: url, threadRootEventID: threadRootEventID)) - case .presentLocationPicker: - stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker)) + case .presentLocationPicker(let threadRootEventID): + stateMachine.tryEvent(.presentMapNavigator(interactionMode: .picker, + threadRootEventID: threadRootEventID)) case .presentPollForm(let mode): stateMachine.tryEvent(.presentPollForm(mode: mode)) - case .presentLocationViewer(_, let geoURI, let description): - stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, description: description))) + case .presentLocationViewer(_, let geoURI, let description, let threadRootEventID): + stateMachine.tryEvent(.presentMapNavigator(interactionMode: .viewOnly(geoURI: geoURI, + description: description), + threadRootEventID: threadRootEventID)) case .presentEmojiPicker(let itemID, let selectedEmojis): - stateMachine.tryEvent(.presentEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis)) + stateMachine.tryEvent(.presentEmojiPicker(itemID: itemID, + selectedEmojis: selectedEmojis)) case .presentRoomMemberDetails(let userID): stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID)) case .presentMessageForwarding(let forwardingItem): stateMachine.tryEvent(.presentMessageForwarding(forwardingItem: forwardingItem)) case .presentResolveSendFailure(let failure, let sendHandle): - stateMachine.tryEvent(.presentResolveSendFailure(failure: failure, sendHandle: sendHandle)) + stateMachine.tryEvent(.presentResolveSendFailure(failure: failure, + sendHandle: sendHandle)) } } .store(in: &cancellables) @@ -961,7 +975,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { } } - private func presentMapNavigator(interactionMode: StaticLocationInteractionMode) { + private func presentMapNavigator(interactionMode: StaticLocationInteractionMode, + threadRootEventID: String?) { let stackCoordinator = NavigationStackCoordinator() let params = StaticLocationScreenCoordinatorParameters(interactionMode: interactionMode, @@ -974,13 +989,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { switch action { case .selectedLocation(let geoURI, let isUserLocation): Task { - #warning("Allow sending locations within threads when the SDK allows it") _ = await self.roomProxy.timeline.sendLocation(body: geoURI.bodyMessage, geoURI: geoURI, description: nil, zoomLevel: 15, assetType: isUserLocation ? .sender : .pin, - threadRootEventID: nil) + threadRootEventID: threadRootEventID) self.navigationStackCoordinator.setSheetCoordinator(nil) } @@ -1038,7 +1052,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { private func createPoll(question: String, options: [String], pollKind: Poll.Kind) { Task { - #warning("Allow sending polls within threads when the SDK allows it") let result = await roomProxy.timeline.createPoll(question: question, answers: options, pollKind: pollKind, threadRootEventID: nil) self.analytics.trackComposer(inThread: false, diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift index a31c9e04e..c8ed2b56c 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinatorStateMachine.swift @@ -60,7 +60,7 @@ extension RoomFlowCoordinator { case mediaUploadPicker(source: MediaPickerScreenSource, threadRootEventID: String?, previousState: State) case mediaUploadPreview(fileURL: URL, threadRootEventID: String?, previousState: State) case emojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set, previousState: State) - case mapNavigator(previousState: State) + case mapNavigator(threadRootEventID: String?, previousState: State) case messageForwarding(forwardingItem: MessageForwardingItem, previousState: State) case reportContent(itemID: TimelineItemIdentifier, senderID: String, previousState: State) case pollForm(previousState: State) @@ -131,7 +131,7 @@ extension RoomFlowCoordinator { case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set) case dismissEmojiPicker - case presentMapNavigator(interactionMode: StaticLocationInteractionMode) + case presentMapNavigator(interactionMode: StaticLocationInteractionMode, threadRootEventID: String?) case dismissMapNavigator case presentMessageForwarding(forwardingItem: MessageForwardingItem) @@ -196,8 +196,8 @@ extension RoomFlowCoordinator { case (.room, .presentMessageForwarding(let forwardingItem)): return .messageForwarding(forwardingItem: forwardingItem, previousState: fromState) - case (.room, .presentMapNavigator): - return .mapNavigator(previousState: fromState) + case (.room, .presentMapNavigator(_, let threadRootEventID)): + return .mapNavigator(threadRootEventID: threadRootEventID, previousState: fromState) case (.room, .presentPollForm): return .pollForm(previousState: fromState) @@ -233,8 +233,8 @@ extension RoomFlowCoordinator { case (.thread, .presentMessageForwarding(let forwardingItem)): return .messageForwarding(forwardingItem: forwardingItem, previousState: fromState) - case (.thread, .presentMapNavigator): - return .mapNavigator(previousState: fromState) + case (.thread, .presentMapNavigator(_, let threadRootEventID)): + return .mapNavigator(threadRootEventID: threadRootEventID, previousState: fromState) case (.thread, .presentPollForm): return .pollForm(previousState: fromState) @@ -256,7 +256,7 @@ extension RoomFlowCoordinator { case (.messageForwarding(_, let previousState), .dismissMessageForwarding): return previousState - case (.mapNavigator(let previousState), .dismissMapNavigator): + case (.mapNavigator(_, let previousState), .dismissMapNavigator): return previousState case (.pollForm(let previousState), .dismissPollForm): diff --git a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift index b29f08a03..84df4568a 100644 --- a/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/SDKGeneratedMocks.swift @@ -22215,15 +22215,16 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline, @unchecked Sendable { //MARK: - sendLocation - var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeUnderlyingCallsCount = 0 - open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeCallsCount: Int { + open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsThrowableError: Error? + var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsUnderlyingCallsCount = 0 + open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsCallsCount: Int { get { if Thread.isMainThread { - return sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeUnderlyingCallsCount + return sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeUnderlyingCallsCount + returnValue = sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsUnderlyingCallsCount } return returnValue! @@ -22231,28 +22232,31 @@ open class TimelineSDKMock: MatrixRustSDK.Timeline, @unchecked Sendable { } set { if Thread.isMainThread { - sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeUnderlyingCallsCount = newValue + sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeUnderlyingCallsCount = newValue + sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsUnderlyingCallsCount = newValue } } } } - open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeCalled: Bool { - return sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeCallsCount > 0 + open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsCalled: Bool { + return sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsCallsCount > 0 } - open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReceivedArguments: (body: String, geoUri: String, description: String?, zoomLevel: UInt8?, assetType: AssetType?)? - open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReceivedInvocations: [(body: String, geoUri: String, description: String?, zoomLevel: UInt8?, assetType: AssetType?)] = [] - open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeClosure: ((String, String, String?, UInt8?, AssetType?) async -> Void)? + open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsReceivedArguments: (body: String, geoUri: String, description: String?, zoomLevel: UInt8?, assetType: AssetType?, replyParams: ReplyParameters?)? + open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsReceivedInvocations: [(body: String, geoUri: String, description: String?, zoomLevel: UInt8?, assetType: AssetType?, replyParams: ReplyParameters?)] = [] + open var sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsClosure: ((String, String, String?, UInt8?, AssetType?, ReplyParameters?) async throws -> Void)? - open override func sendLocation(body: String, geoUri: String, description: String?, zoomLevel: UInt8?, assetType: AssetType?) async { - sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeCallsCount += 1 - sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReceivedArguments = (body: body, geoUri: geoUri, description: description, zoomLevel: zoomLevel, assetType: assetType) - DispatchQueue.main.async { - self.sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReceivedInvocations.append((body: body, geoUri: geoUri, description: description, zoomLevel: zoomLevel, assetType: assetType)) + open override func sendLocation(body: String, geoUri: String, description: String?, zoomLevel: UInt8?, assetType: AssetType?, replyParams: ReplyParameters?) async throws { + if let error = sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsThrowableError { + throw error } - await sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeClosure?(body, geoUri, description, zoomLevel, assetType) + sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsCallsCount += 1 + sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsReceivedArguments = (body: body, geoUri: geoUri, description: description, zoomLevel: zoomLevel, assetType: assetType, replyParams: replyParams) + DispatchQueue.main.async { + self.sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsReceivedInvocations.append((body: body, geoUri: geoUri, description: description, zoomLevel: zoomLevel, assetType: assetType, replyParams: replyParams)) + } + try await sendLocationBodyGeoUriDescriptionZoomLevelAssetTypeReplyParamsClosure?(body, geoUri, description, zoomLevel, assetType, replyParams) } //MARK: - sendPollResponse diff --git a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift index 02955f7f6..45bc71185 100644 --- a/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift +++ b/ElementX/Sources/Screens/RoomScreen/ComposerToolbar/View/RoomAttachmentPicker.swift @@ -49,7 +49,7 @@ struct RoomAttachmentPicker: View { .accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerPoll) } - if !context.viewState.isInThread, context.viewState.isLocationSharingEnabled { + if context.viewState.isLocationSharingEnabled { Button { context.send(viewAction: .attach(.location)) } label: { diff --git a/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenCoordinator.swift b/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenCoordinator.swift index cc7c7bff7..170c95014 100644 --- a/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenCoordinator.swift +++ b/ElementX/Sources/Screens/ThreadTimelineScreen/ThreadTimelineScreenCoordinator.swift @@ -29,9 +29,9 @@ enum ThreadTimelineScreenCoordinatorAction { case presentReportContent(itemID: TimelineItemIdentifier, senderID: String) case presentMediaUploadPicker(MediaPickerScreenSource, threadRootEventID: String?) case presentMediaUploadPreviewScreen(url: URL, threadRootEventID: String?) - case presentLocationPicker + case presentLocationPicker(threadRootEventID: String?) + case presentLocationViewer(body: String, geoURI: GeoURI, description: String?, threadRootEventID: String?) case presentPollForm(mode: PollFormMode) - case presentLocationViewer(body: String, geoURI: GeoURI, description: String?) case presentEmojiPicker(itemID: TimelineItemIdentifier, selectedEmojis: Set) case presentRoomMemberDetails(userID: String) case presentMessageForwarding(forwardingItem: MessageForwardingItem) @@ -99,27 +99,34 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol { case .displayReportContent(let itemID, let senderID): actionsSubject.send(.presentReportContent(itemID: itemID, senderID: senderID)) case .displayCameraPicker: - actionsSubject.send(.presentMediaUploadPicker(.camera, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) + actionsSubject.send(.presentMediaUploadPicker(.camera, + threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) case .displayMediaPicker: - actionsSubject.send(.presentMediaUploadPicker(.photoLibrary, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) + actionsSubject.send(.presentMediaUploadPicker(.photoLibrary, + threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) case .displayDocumentPicker: - actionsSubject.send(.presentMediaUploadPicker(.documents, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) + actionsSubject.send(.presentMediaUploadPicker(.documents, + threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) case .displayMediaPreview(let mediaPreviewViewModel): viewModel.displayMediaPreview(mediaPreviewViewModel) case .displayLocationPicker: - actionsSubject.send(.presentLocationPicker) + actionsSubject.send(.presentLocationPicker(threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) + case .displayLocation(let body, let geoURI, let description): + actionsSubject.send(.presentLocationViewer(body: body, + geoURI: geoURI, + description: description, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) case .displayPollForm(let mode): actionsSubject.send(.presentPollForm(mode: mode)) case .displayMediaUploadPreviewScreen(let url): - actionsSubject.send(.presentMediaUploadPreviewScreen(url: url, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) + actionsSubject.send(.presentMediaUploadPreviewScreen(url: url, + threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID)) case .displaySenderDetails(userID: let userID): actionsSubject.send(.presentRoomMemberDetails(userID: userID)) case .displayMessageForwarding(let forwardingItem): actionsSubject.send(.presentMessageForwarding(forwardingItem: forwardingItem)) - case .displayLocation(let body, let geoURI, let description): - actionsSubject.send(.presentLocationViewer(body: body, geoURI: geoURI, description: description)) case .displayResolveSendFailure(let failure, let sendHandle): - actionsSubject.send(.presentResolveSendFailure(failure: failure, sendHandle: sendHandle)) + actionsSubject.send(.presentResolveSendFailure(failure: failure, + sendHandle: sendHandle)) case .hasScrolled, .displayRoom: break case .composer(let action): diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index a0d9468c5..3b1543155 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -341,13 +341,25 @@ final class TimelineProxy: TimelineProxyProtocol { threadRootEventID: String?) async -> Result { MXLog.info("Sending location") - await timeline.sendLocation(body: body, - geoUri: geoURI.string, - description: description, - zoomLevel: zoomLevel, - assetType: assetType) + let replyParameters: ReplyParameters? = if let threadRootEventID { + ReplyParameters(eventId: threadRootEventID, enforceThread: true, replyWithinThread: false) + } else { + nil + } - MXLog.info("Finished sending location") + do { + try await timeline.sendLocation(body: body, + geoUri: geoURI.string, + description: description, + zoomLevel: zoomLevel, + assetType: assetType, + replyParams: replyParameters) + + MXLog.info("Finished sending location") + } catch { + MXLog.error("Failed sending location with error: \(error)") + return .failure(.sdkError(error)) + } return .success(()) } diff --git a/project.yml b/project.yml index a51e934d7..15d86d701 100644 --- a/project.yml +++ b/project.yml @@ -65,7 +65,7 @@ packages: # Element/Matrix dependencies MatrixRustSDK: url: https://github.com/element-hq/matrix-rust-components-swift - exactVersion: 25.06.11 + exactVersion: 25.06.12 # path: ../matrix-rust-sdk Compound: url: https://github.com/element-hq/compound-ios