mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Support sending gallery messages behind a feature flag (#5909)
dds sending of multi-media "gallery" messages, gated behind the new galleryEnabled developer option (Developer Options → Room, off by default). Rendering of received galleries ships in a separate PR. Wires sendGallery through TimelineProxy/TimelineController onto the SDK's Timeline.sendGallery. MediaUploadPreviewScreen sends selected items as one gallery when the flag is on. Co-authored with the original author of #5601. NOTE: This PR allows to send gallery messages, but they won't be rendered without the gallery timeline item PR.
This commit is contained in:
@@ -177,7 +177,7 @@ final class MediaUploadPreviewScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func multipleFiles() async throws {
|
||||
func gallery() async throws {
|
||||
// Given an upload screen with multiple media files.
|
||||
setUpViewModel(urls: [fileURL, imageURL, fileURL], expectedCaption: nil)
|
||||
#expect(!context.viewState.shouldDisableInteraction)
|
||||
@@ -190,15 +190,17 @@ final class MediaUploadPreviewScreenViewModelTests {
|
||||
#expect(context.viewState.shouldDisableInteraction, "The interaction should be disabled while sending.")
|
||||
#expect(userIndicatorController.submitIndicatorDelayCallsCount == 1) // Loading indicator
|
||||
|
||||
// Then the screen should be dismissed once all of the files have been sent.
|
||||
// Then the screen should be dismissed once the gallery has been sent in a single request.
|
||||
try await deferredDismiss.fulfill()
|
||||
#expect(timelineProxy.sendImageUrlThumbnailURLImageInfoCaptionRequestHandleCallsCount == 1)
|
||||
#expect(timelineProxy.sendFileUrlFileInfoCaptionRequestHandleCallsCount == 2)
|
||||
#expect(timelineProxy.sendGalleryItemInfosCaptionInReplyToEventIDCallsCount == 1)
|
||||
#expect(timelineProxy.sendGalleryItemInfosCaptionInReplyToEventIDReceivedArguments?.itemInfos.count == 3)
|
||||
#expect(timelineProxy.sendImageUrlThumbnailURLImageInfoCaptionRequestHandleCallsCount == 0)
|
||||
#expect(timelineProxy.sendFileUrlFileInfoCaptionRequestHandleCallsCount == 0)
|
||||
#expect(userIndicatorController.submitIndicatorDelayCallsCount == 1, "Only a loading indicator should be shown.")
|
||||
}
|
||||
|
||||
@Test
|
||||
func multipleFilesWithProcessingFailure() async throws {
|
||||
func galleryWithProcessingFailure() async throws {
|
||||
// Given an upload screen for a non-existent file.
|
||||
setUpViewModel(urls: [imageURL, fileURL, badImageURL], expectedCaption: nil)
|
||||
#expect(!context.viewState.shouldDisableInteraction)
|
||||
@@ -217,9 +219,9 @@ final class MediaUploadPreviewScreenViewModelTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
func multipleFilesWithSendFailure() async throws {
|
||||
// Given an upload screen with multiple media files where one of the files will fail to send.
|
||||
setUpViewModel(urls: [fileURL, imageURL, imageURL, fileURL], expectedCaption: nil, simulateImageSendFailures: true)
|
||||
func galleryWithSendFailure() async throws {
|
||||
// Given an upload screen with multiple media files where the gallery send fails.
|
||||
setUpViewModel(urls: [fileURL, imageURL, imageURL, fileURL], expectedCaption: nil, simulateGallerySendFailure: true)
|
||||
#expect(!context.viewState.shouldDisableInteraction)
|
||||
#expect(userIndicatorController.submitIndicatorDelayCallsCount == 0)
|
||||
|
||||
@@ -230,11 +232,27 @@ final class MediaUploadPreviewScreenViewModelTests {
|
||||
#expect(context.viewState.shouldDisableInteraction, "The interaction should be disabled while sending.")
|
||||
#expect(userIndicatorController.submitIndicatorDelayCallsCount == 1) // Loading indicator
|
||||
|
||||
// Then the screen should be dismissed so the user can see which files made it into the timeline.
|
||||
// Then the screen should still be dismissed and an error indicator surfaced.
|
||||
try await deferredDismiss.fulfill()
|
||||
#expect(timelineProxy.sendImageUrlThumbnailURLImageInfoCaptionRequestHandleCallsCount == 2)
|
||||
#expect(timelineProxy.sendGalleryItemInfosCaptionInReplyToEventIDCallsCount == 1)
|
||||
#expect(userIndicatorController.submitIndicatorDelayCallsCount == 2, "An error indicator should be shown.")
|
||||
}
|
||||
|
||||
@Test
|
||||
func galleryDisabledSendsIndividualMessages() async throws {
|
||||
// Given an upload screen with multiple media files and gallery sending disabled.
|
||||
setUpViewModel(urls: [fileURL, imageURL, fileURL], expectedCaption: nil, galleryEnabled: false)
|
||||
#expect(!context.viewState.shouldDisableInteraction)
|
||||
|
||||
// When attempting to send the files.
|
||||
let deferredDismiss = deferFulfillment(viewModel.actions) { $0 == .dismiss }
|
||||
context.send(viewAction: .send)
|
||||
|
||||
// Then each file should be sent as an individual message rather than a single gallery.
|
||||
try await deferredDismiss.fulfill()
|
||||
#expect(timelineProxy.sendGalleryItemInfosCaptionInReplyToEventIDCallsCount == 0)
|
||||
#expect(timelineProxy.sendFileUrlFileInfoCaptionRequestHandleCallsCount == 2)
|
||||
#expect(userIndicatorController.submitIndicatorDelayCallsCount == 3, "Error indicators for each failure should be shown.")
|
||||
#expect(timelineProxy.sendImageUrlThumbnailURLImageInfoCaptionRequestHandleCallsCount == 1)
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
@@ -268,7 +286,8 @@ final class MediaUploadPreviewScreenViewModelTests {
|
||||
private func setUpViewModel(urls: [URL],
|
||||
expectedCaption: String?,
|
||||
maxUploadSizeResult: Result<UInt, ClientProxyError>? = nil,
|
||||
simulateImageSendFailures: Bool = false) {
|
||||
simulateGallerySendFailure: Bool = false,
|
||||
galleryEnabled: Bool = true) {
|
||||
timelineProxy = TimelineProxyMock(.init())
|
||||
timelineProxy.sendAudioUrlAudioInfoCaptionRequestHandleClosure = { [weak self] _, _, caption, _ in
|
||||
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||
@@ -277,12 +296,15 @@ final class MediaUploadPreviewScreenViewModelTests {
|
||||
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||
}
|
||||
timelineProxy.sendImageUrlThumbnailURLImageInfoCaptionRequestHandleClosure = { [weak self] _, _, _, caption, _ in
|
||||
guard !simulateImageSendFailures else { return .failure(.sdkError(TestError.unknown)) }
|
||||
return self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||
}
|
||||
timelineProxy.sendVideoUrlThumbnailURLVideoInfoCaptionRequestHandleClosure = { [weak self] _, _, _, caption, _ in
|
||||
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||
}
|
||||
timelineProxy.sendGalleryItemInfosCaptionInReplyToEventIDClosure = { [weak self] _, caption, _ in
|
||||
guard !simulateGallerySendFailure else { return .failure(.sdkError(TestError.unknown)) }
|
||||
return self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||
}
|
||||
|
||||
clientProxy = ClientProxyMock(.init())
|
||||
if let maxUploadSizeResult {
|
||||
@@ -293,6 +315,7 @@ final class MediaUploadPreviewScreenViewModelTests {
|
||||
caption: nil,
|
||||
title: "Some File",
|
||||
shouldShowCaptionWarning: true,
|
||||
galleryEnabled: galleryEnabled,
|
||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
||||
timelineController: TimelineControllerMock(.init(timelineProxy: timelineProxy)),
|
||||
clientProxy: clientProxy,
|
||||
|
||||
Reference in New Issue
Block a user