mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Add support for sending media within threads.
This patch add the `threadRootEventID` as a parameter to media sending functions and forwards the timeline thread root between various application states to the place where its needed. The 2 missing items are Locations and Polls which currently don't have any support on the Rust side for being sent within threads. That will come in a separate PR.
This commit is contained in:
committed by
Stefan Ceriu
parent
6481093107
commit
558cdf5be3
+2
@@ -14,6 +14,7 @@ struct MediaUploadPreviewScreenCoordinatorParameters {
|
||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
let title: String?
|
||||
let url: URL
|
||||
let threadRootEventID: String?
|
||||
let shouldShowCaptionWarning: Bool
|
||||
}
|
||||
|
||||
@@ -36,6 +37,7 @@ final class MediaUploadPreviewScreenCoordinator: CoordinatorProtocol {
|
||||
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
||||
title: parameters.title,
|
||||
url: parameters.url,
|
||||
threadRootEventID: parameters.threadRootEventID,
|
||||
shouldShowCaptionWarning: parameters.shouldShowCaptionWarning)
|
||||
}
|
||||
|
||||
|
||||
+11
-2
@@ -16,6 +16,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
private let roomProxy: JoinedRoomProxyProtocol
|
||||
private let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||
private let url: URL
|
||||
private let threadRootEventID: String?
|
||||
|
||||
private var processingTask: Task<Result<MediaInfo, MediaUploadingPreprocessorError>, Never>
|
||||
private var requestHandle: SendAttachmentJoinHandleProtocol?
|
||||
@@ -31,11 +32,13 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
||||
title: String?,
|
||||
url: URL,
|
||||
threadRootEventID: String?,
|
||||
shouldShowCaptionWarning: Bool) {
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.roomProxy = roomProxy
|
||||
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
||||
self.url = url
|
||||
self.threadRootEventID = threadRootEventID
|
||||
|
||||
// Start processing the media whilst the user is reviewing it/adding a caption.
|
||||
processingTask = Task { await mediaUploadingPreprocessor.processMedia(at: url) }
|
||||
@@ -57,7 +60,9 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
Task {
|
||||
switch await processingTask.value {
|
||||
case .success(let mediaInfo):
|
||||
switch await sendAttachment(mediaInfo: mediaInfo, caption: caption) {
|
||||
switch await sendAttachment(mediaInfo: mediaInfo,
|
||||
caption: caption,
|
||||
threadRootEventID: threadRootEventID) {
|
||||
case .success:
|
||||
actionsSubject.send(.dismiss)
|
||||
case .failure(let error):
|
||||
@@ -85,7 +90,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func sendAttachment(mediaInfo: MediaInfo, caption: String?) async -> Result<Void, TimelineProxyError> {
|
||||
private func sendAttachment(mediaInfo: MediaInfo, caption: String?, threadRootEventID: String?) async -> Result<Void, TimelineProxyError> {
|
||||
let requestHandle: ((SendAttachmentJoinHandleProtocol) -> Void) = { [weak self] handle in
|
||||
self?.requestHandle = handle
|
||||
}
|
||||
@@ -96,22 +101,26 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
||||
thumbnailURL: thumbnailURL,
|
||||
imageInfo: imageInfo,
|
||||
caption: caption,
|
||||
threadRootEventID: threadRootEventID,
|
||||
requestHandle: requestHandle)
|
||||
case let .video(videoURL, thumbnailURL, videoInfo):
|
||||
return await roomProxy.timeline.sendVideo(url: videoURL,
|
||||
thumbnailURL: thumbnailURL,
|
||||
videoInfo: videoInfo,
|
||||
caption: caption,
|
||||
threadRootEventID: threadRootEventID,
|
||||
requestHandle: requestHandle)
|
||||
case let .audio(audioURL, audioInfo):
|
||||
return await roomProxy.timeline.sendAudio(url: audioURL,
|
||||
audioInfo: audioInfo,
|
||||
caption: caption,
|
||||
threadRootEventID: threadRootEventID,
|
||||
requestHandle: requestHandle)
|
||||
case let .file(fileURL, fileInfo):
|
||||
return await roomProxy.timeline.sendFile(url: fileURL,
|
||||
fileInfo: fileInfo,
|
||||
caption: caption,
|
||||
threadRootEventID: threadRootEventID,
|
||||
requestHandle: requestHandle)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,6 +232,7 @@ struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview {
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||
title: "App Icon.png",
|
||||
url: snapshotURL,
|
||||
threadRootEventID: nil,
|
||||
shouldShowCaptionWarning: true)
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
|
||||
@@ -80,6 +80,8 @@ struct ComposerToolbarViewState: BindableState {
|
||||
|
||||
var keyCommands: [WysiwygKeyCommand] = []
|
||||
|
||||
var isInThread: Bool
|
||||
|
||||
var bindings: ComposerToolbarViewStateBindings
|
||||
|
||||
var isUploading: Bool {
|
||||
|
||||
@@ -46,6 +46,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
|
||||
|
||||
init(initialText: String? = nil,
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
isInThread: Bool = false,
|
||||
wysiwygViewModel: WysiwygComposerViewModel,
|
||||
completionSuggestionService: CompletionSuggestionServiceProtocol,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
@@ -68,6 +69,7 @@ final class ComposerToolbarViewModel: ComposerToolbarViewModelType, ComposerTool
|
||||
audioRecorderState: .init(),
|
||||
isRoomEncrypted: roomProxy.infoPublisher.value.isEncrypted,
|
||||
isLocationSharingEnabled: appSettings.mapTilerConfiguration.isEnabled,
|
||||
isInThread: isInThread,
|
||||
bindings: .init()),
|
||||
mediaProvider: mediaProvider)
|
||||
|
||||
|
||||
@@ -40,14 +40,16 @@ struct RoomAttachmentPicker: View {
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerTextFormatting)
|
||||
|
||||
Button {
|
||||
context.send(viewAction: .attach(.poll))
|
||||
} label: {
|
||||
Label(L10n.screenRoomAttachmentSourcePoll, icon: \.polls)
|
||||
if !context.viewState.isInThread {
|
||||
Button {
|
||||
context.send(viewAction: .attach(.poll))
|
||||
} label: {
|
||||
Label(L10n.screenRoomAttachmentSourcePoll, icon: \.polls)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerPoll)
|
||||
}
|
||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerPoll)
|
||||
|
||||
if context.viewState.isLocationSharingEnabled {
|
||||
if !context.viewState.isInThread, context.viewState.isLocationSharingEnabled {
|
||||
Button {
|
||||
context.send(viewAction: .attach(.location))
|
||||
} label: {
|
||||
|
||||
@@ -98,6 +98,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
parserStyle: .elementX)
|
||||
let composerViewModel = ComposerToolbarViewModel(initialText: parameters.sharedText,
|
||||
roomProxy: parameters.roomProxy,
|
||||
isInThread: false,
|
||||
wysiwygViewModel: wysiwygViewModel,
|
||||
completionSuggestionService: parameters.completionSuggestionService,
|
||||
mediaProvider: parameters.mediaProvider,
|
||||
|
||||
@@ -27,8 +27,8 @@ struct ThreadTimelineScreenCoordinatorParameters {
|
||||
|
||||
enum ThreadTimelineScreenCoordinatorAction {
|
||||
case presentReportContent(itemID: TimelineItemIdentifier, senderID: String)
|
||||
case presentMediaUploadPicker(MediaPickerScreenSource)
|
||||
case presentMediaUploadPreviewScreen(URL)
|
||||
case presentMediaUploadPicker(MediaPickerScreenSource, threadRootEventID: String?)
|
||||
case presentMediaUploadPreviewScreen(url: URL, threadRootEventID: String?)
|
||||
case presentLocationPicker
|
||||
case presentPollForm(mode: PollFormMode)
|
||||
case presentLocationViewer(body: String, geoURI: GeoURI, description: String?)
|
||||
@@ -78,6 +78,7 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
|
||||
|
||||
composerViewModel = ComposerToolbarViewModel(initialText: nil,
|
||||
roomProxy: parameters.roomProxy,
|
||||
isInThread: true,
|
||||
wysiwygViewModel: wysiwygViewModel,
|
||||
completionSuggestionService: parameters.completionSuggestionService,
|
||||
mediaProvider: parameters.mediaProvider,
|
||||
@@ -98,11 +99,11 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
|
||||
case .displayReportContent(let itemID, let senderID):
|
||||
actionsSubject.send(.presentReportContent(itemID: itemID, senderID: senderID))
|
||||
case .displayCameraPicker:
|
||||
actionsSubject.send(.presentMediaUploadPicker(.camera))
|
||||
actionsSubject.send(.presentMediaUploadPicker(.camera, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID))
|
||||
case .displayMediaPicker:
|
||||
actionsSubject.send(.presentMediaUploadPicker(.photoLibrary))
|
||||
actionsSubject.send(.presentMediaUploadPicker(.photoLibrary, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID))
|
||||
case .displayDocumentPicker:
|
||||
actionsSubject.send(.presentMediaUploadPicker(.documents))
|
||||
actionsSubject.send(.presentMediaUploadPicker(.documents, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID))
|
||||
case .displayMediaPreview(let mediaPreviewViewModel):
|
||||
viewModel.displayMediaPreview(mediaPreviewViewModel)
|
||||
case .displayLocationPicker:
|
||||
@@ -110,7 +111,7 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
|
||||
case .displayPollForm(let mode):
|
||||
actionsSubject.send(.presentPollForm(mode: mode))
|
||||
case .displayMediaUploadPreviewScreen(let url):
|
||||
actionsSubject.send(.presentMediaUploadPreviewScreen(url))
|
||||
actionsSubject.send(.presentMediaUploadPreviewScreen(url: url, threadRootEventID: parameters.timelineController.timelineKind.threadRootEventID))
|
||||
case .displaySenderDetails(userID: let userID):
|
||||
actionsSubject.send(.presentRoomMemberDetails(userID: userID))
|
||||
case .displayMessageForwarding(let forwardingItem):
|
||||
|
||||
@@ -362,7 +362,9 @@ class TimelineInteractionHandler {
|
||||
|
||||
actionsSubject.send(.composer(action: .setMode(mode: .previewVoiceMessage(state: audioPlayerState, waveform: .url(recordingURL), isUploading: true))))
|
||||
await voiceMessageRecorder.stopPlayback()
|
||||
switch await voiceMessageRecorder.sendVoiceMessage(inRoom: roomProxy, audioConverter: AudioConverter()) {
|
||||
switch await voiceMessageRecorder.sendVoiceMessage(inRoom: roomProxy,
|
||||
audioConverter: AudioConverter(),
|
||||
threadRootEventID: timelineController.timelineKind.threadRootEventID) {
|
||||
case .success:
|
||||
await deleteCurrentVoiceMessage()
|
||||
case .failure(let error):
|
||||
|
||||
Reference in New Issue
Block a user