mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
Support adding a caption to media uploads. (#3531)
* Add a composer to the MediaUploadPreviewScreen. And send it's content to the media upload's caption. * Use the new compound SendButton (updating relative padding in the toolbar). * Update snapshots. * Add unit tests for sending a caption.
This commit is contained in:
parent
352bb577ad
commit
b75ad6a5aa
@ -689,6 +689,7 @@
|
|||||||
90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
|
90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
|
||||||
914BDF61447C723F104BCE33 /* SessionDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C2067FF58B4996323EB40C /* SessionDirectories.swift */; };
|
914BDF61447C723F104BCE33 /* SessionDirectories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43C2067FF58B4996323EB40C /* SessionDirectories.swift */; };
|
||||||
915B4CDAF220D9AEB4047D45 /* PollInteractionHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 259E5B05BDE6E20C26CF11B4 /* PollInteractionHandlerProtocol.swift */; };
|
915B4CDAF220D9AEB4047D45 /* PollInteractionHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 259E5B05BDE6E20C26CF11B4 /* PollInteractionHandlerProtocol.swift */; };
|
||||||
|
919BAE492CECA981009F6A5B /* TimelineProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 919BAE482CECA981009F6A5B /* TimelineProxyMock.swift */; };
|
||||||
91ABC91758A6E4A5FAA2E9C4 /* ReadReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */; };
|
91ABC91758A6E4A5FAA2E9C4 /* ReadReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */; };
|
||||||
91C6AC0E9D2B9C0C76CC6AD4 /* RoomDirectorySearchScreenScreenModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */; };
|
91C6AC0E9D2B9C0C76CC6AD4 /* RoomDirectorySearchScreenScreenModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */; };
|
||||||
91D1A46A733EC24C081DD353 /* SessionVerificationRequestDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1265FAF2C0AF1C30605BE7 /* SessionVerificationRequestDetailsView.swift */; };
|
91D1A46A733EC24C081DD353 /* SessionVerificationRequestDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A1265FAF2C0AF1C30605BE7 /* SessionVerificationRequestDetailsView.swift */; };
|
||||||
@ -1900,6 +1901,7 @@
|
|||||||
90F2F8998E5632668B0AD848 /* RoomTimelineItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemView.swift; sourceTree = "<group>"; };
|
90F2F8998E5632668B0AD848 /* RoomTimelineItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemView.swift; sourceTree = "<group>"; };
|
||||||
913C8E13B8B602C7B6C0C4AE /* PillTextAttachmentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachmentData.swift; sourceTree = "<group>"; };
|
913C8E13B8B602C7B6C0C4AE /* PillTextAttachmentData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillTextAttachmentData.swift; sourceTree = "<group>"; };
|
||||||
91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenCoordinator.swift; sourceTree = "<group>"; };
|
91868EB98818044E6FEBE532 /* NotificationPermissionsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
|
919BAE482CECA981009F6A5B /* TimelineProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProxyMock.swift; sourceTree = "<group>"; };
|
||||||
91C8BD78F7B9247AC57FA1A3 /* RedactedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineView.swift; sourceTree = "<group>"; };
|
91C8BD78F7B9247AC57FA1A3 /* RedactedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
91CF6F7D08228D16BA69B63B /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
91CF6F7D08228D16BA69B63B /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
91FFE1F410969ECB23FE9BB2 /* TimelineItemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMenu.swift; sourceTree = "<group>"; };
|
91FFE1F410969ECB23FE9BB2 /* TimelineItemMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemMenu.swift; sourceTree = "<group>"; };
|
||||||
@ -3032,6 +3034,7 @@
|
|||||||
9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */,
|
9EB9BA2F30EB8C33226D8FF1 /* UserSessionStoreMock.swift */,
|
||||||
B23135B06B044CB811139D2F /* Generated */,
|
B23135B06B044CB811139D2F /* Generated */,
|
||||||
E5E545F92D01588360A9BAC5 /* SDK */,
|
E5E545F92D01588360A9BAC5 /* SDK */,
|
||||||
|
919BAE482CECA981009F6A5B /* TimelineProxyMock.swift */,
|
||||||
);
|
);
|
||||||
path = Mocks;
|
path = Mocks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -7096,6 +7099,7 @@
|
|||||||
50C90117FE25390BFBD40173 /* RustTracing.swift in Sources */,
|
50C90117FE25390BFBD40173 /* RustTracing.swift in Sources */,
|
||||||
D43F0503EF2CBC55272538FE /* SDKGeneratedMocks.swift in Sources */,
|
D43F0503EF2CBC55272538FE /* SDKGeneratedMocks.swift in Sources */,
|
||||||
88CBF1595E39CE697928DE48 /* SFNumberedListView.swift in Sources */,
|
88CBF1595E39CE697928DE48 /* SFNumberedListView.swift in Sources */,
|
||||||
|
919BAE492CECA981009F6A5B /* TimelineProxyMock.swift in Sources */,
|
||||||
FB595EC9C00AB32F39034055 /* SceneDelegate.swift in Sources */,
|
FB595EC9C00AB32F39034055 /* SceneDelegate.swift in Sources */,
|
||||||
0437765FF480249486893CC7 /* ScreenTrackerViewModifier.swift in Sources */,
|
0437765FF480249486893CC7 /* ScreenTrackerViewModifier.swift in Sources */,
|
||||||
0BFA67AFD757EE2BA569836A /* ScrollViewAdapter.swift in Sources */,
|
0BFA67AFD757EE2BA569836A /* ScrollViewAdapter.swift in Sources */,
|
||||||
|
@ -248,8 +248,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
|
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "7b0bbbae90c41f848f90ac7b4df6c4f50068256d",
|
"revision" : "42a086182681cf661f5c47c9b7dc3931de18c6d7",
|
||||||
"version" : "1.17.5"
|
"version" : "1.17.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -14449,15 +14449,15 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
//MARK: - sendAudio
|
//MARK: - sendAudio
|
||||||
|
|
||||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingCallsCount = 0
|
var sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = 0
|
||||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount: Int {
|
var sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleCallsCount: Int {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingCallsCount
|
return sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Int? = nil
|
var returnValue: Int? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingCallsCount
|
returnValue = sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@ -14465,27 +14465,27 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleCalled: Bool {
|
var sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleCalled: Bool {
|
||||||
return sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount > 0
|
return sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleCallsCount > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
var sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
||||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>! {
|
var sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>! {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingReturnValue
|
return sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Result<Void, TimelineProxyError>? = nil
|
var returnValue: Result<Void, TimelineProxyError>? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingReturnValue
|
returnValue = sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@ -14493,35 +14493,35 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
sendAudioUrlAudioInfoProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure: ((URL, AudioInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
var sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleClosure: ((URL, AudioInfo, String?, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||||
|
|
||||||
func sendAudio(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
func sendAudio(url: URL, audioInfo: AudioInfo, caption: String?, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||||
sendAudioUrlAudioInfoProgressSubjectRequestHandleCallsCount += 1
|
sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleCallsCount += 1
|
||||||
if let sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure = sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure {
|
if let sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleClosure = sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleClosure {
|
||||||
return await sendAudioUrlAudioInfoProgressSubjectRequestHandleClosure(url, audioInfo, progressSubject, requestHandle)
|
return await sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleClosure(url, audioInfo, caption, progressSubject, requestHandle)
|
||||||
} else {
|
} else {
|
||||||
return sendAudioUrlAudioInfoProgressSubjectRequestHandleReturnValue
|
return sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//MARK: - sendFile
|
//MARK: - sendFile
|
||||||
|
|
||||||
var sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingCallsCount = 0
|
var sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = 0
|
||||||
var sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount: Int {
|
var sendFileUrlFileInfoCaptionProgressSubjectRequestHandleCallsCount: Int {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingCallsCount
|
return sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Int? = nil
|
var returnValue: Int? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingCallsCount
|
returnValue = sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@ -14529,27 +14529,27 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sendFileUrlFileInfoProgressSubjectRequestHandleCalled: Bool {
|
var sendFileUrlFileInfoCaptionProgressSubjectRequestHandleCalled: Bool {
|
||||||
return sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount > 0
|
return sendFileUrlFileInfoCaptionProgressSubjectRequestHandleCallsCount > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
var sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
||||||
var sendFileUrlFileInfoProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>! {
|
var sendFileUrlFileInfoCaptionProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>! {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingReturnValue
|
return sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Result<Void, TimelineProxyError>? = nil
|
var returnValue: Result<Void, TimelineProxyError>? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingReturnValue
|
returnValue = sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@ -14557,35 +14557,35 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
sendFileUrlFileInfoProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
sendFileUrlFileInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sendFileUrlFileInfoProgressSubjectRequestHandleClosure: ((URL, FileInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
var sendFileUrlFileInfoCaptionProgressSubjectRequestHandleClosure: ((URL, FileInfo, String?, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||||
|
|
||||||
func sendFile(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
func sendFile(url: URL, fileInfo: FileInfo, caption: String?, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||||
sendFileUrlFileInfoProgressSubjectRequestHandleCallsCount += 1
|
sendFileUrlFileInfoCaptionProgressSubjectRequestHandleCallsCount += 1
|
||||||
if let sendFileUrlFileInfoProgressSubjectRequestHandleClosure = sendFileUrlFileInfoProgressSubjectRequestHandleClosure {
|
if let sendFileUrlFileInfoCaptionProgressSubjectRequestHandleClosure = sendFileUrlFileInfoCaptionProgressSubjectRequestHandleClosure {
|
||||||
return await sendFileUrlFileInfoProgressSubjectRequestHandleClosure(url, fileInfo, progressSubject, requestHandle)
|
return await sendFileUrlFileInfoCaptionProgressSubjectRequestHandleClosure(url, fileInfo, caption, progressSubject, requestHandle)
|
||||||
} else {
|
} else {
|
||||||
return sendFileUrlFileInfoProgressSubjectRequestHandleReturnValue
|
return sendFileUrlFileInfoCaptionProgressSubjectRequestHandleReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//MARK: - sendImage
|
//MARK: - sendImage
|
||||||
|
|
||||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingCallsCount = 0
|
var sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = 0
|
||||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount: Int {
|
var sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleCallsCount: Int {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingCallsCount
|
return sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Int? = nil
|
var returnValue: Int? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingCallsCount
|
returnValue = sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@ -14593,27 +14593,27 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCalled: Bool {
|
var sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleCalled: Bool {
|
||||||
return sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount > 0
|
return sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleCallsCount > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
var sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
||||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>! {
|
var sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>! {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingReturnValue
|
return sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Result<Void, TimelineProxyError>? = nil
|
var returnValue: Result<Void, TimelineProxyError>? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingReturnValue
|
returnValue = sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@ -14621,22 +14621,22 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure: ((URL, URL, ImageInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
var sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleClosure: ((URL, URL, ImageInfo, String?, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||||
|
|
||||||
func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, caption: String?, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||||
sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleCallsCount += 1
|
sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleCallsCount += 1
|
||||||
if let sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure = sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure {
|
if let sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleClosure = sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleClosure {
|
||||||
return await sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleClosure(url, thumbnailURL, imageInfo, progressSubject, requestHandle)
|
return await sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleClosure(url, thumbnailURL, imageInfo, caption, progressSubject, requestHandle)
|
||||||
} else {
|
} else {
|
||||||
return sendImageUrlThumbnailURLImageInfoProgressSubjectRequestHandleReturnValue
|
return sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//MARK: - sendLocation
|
//MARK: - sendLocation
|
||||||
@ -14711,15 +14711,15 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
//MARK: - sendVideo
|
//MARK: - sendVideo
|
||||||
|
|
||||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingCallsCount = 0
|
var sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = 0
|
||||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount: Int {
|
var sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleCallsCount: Int {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingCallsCount
|
return sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Int? = nil
|
var returnValue: Int? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingCallsCount
|
returnValue = sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@ -14727,27 +14727,27 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingCallsCount = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCalled: Bool {
|
var sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleCalled: Bool {
|
||||||
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount > 0
|
return sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleCallsCount > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
var sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
||||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>! {
|
var sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleReturnValue: Result<Void, TimelineProxyError>! {
|
||||||
get {
|
get {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingReturnValue
|
return sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue
|
||||||
} else {
|
} else {
|
||||||
var returnValue: Result<Void, TimelineProxyError>? = nil
|
var returnValue: Result<Void, TimelineProxyError>? = nil
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
returnValue = sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingReturnValue
|
returnValue = sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue!
|
return returnValue!
|
||||||
@ -14755,22 +14755,22 @@ class TimelineProxyMock: TimelineProxyProtocol {
|
|||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
if Thread.isMainThread {
|
if Thread.isMainThread {
|
||||||
sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
||||||
} else {
|
} else {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleUnderlyingReturnValue = newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure: ((URL, URL, VideoInfo, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
var sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleClosure: ((URL, URL, VideoInfo, String?, CurrentValueSubject<Double, Never>?, @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>)?
|
||||||
|
|
||||||
func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, caption: String?, progressSubject: CurrentValueSubject<Double, Never>?, requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||||
sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleCallsCount += 1
|
sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleCallsCount += 1
|
||||||
if let sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure = sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure {
|
if let sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleClosure = sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleClosure {
|
||||||
return await sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleClosure(url, thumbnailURL, videoInfo, progressSubject, requestHandle)
|
return await sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleClosure(url, thumbnailURL, videoInfo, caption, progressSubject, requestHandle)
|
||||||
} else {
|
} else {
|
||||||
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectRequestHandleReturnValue
|
return sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleReturnValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//MARK: - sendVoiceMessage
|
//MARK: - sendVoiceMessage
|
||||||
|
@ -49,22 +49,8 @@ extension JoinedRoomProxyMock {
|
|||||||
id = configuration.id
|
id = configuration.id
|
||||||
isEncrypted = configuration.isEncrypted
|
isEncrypted = configuration.isEncrypted
|
||||||
|
|
||||||
let timeline = TimelineProxyMock()
|
timeline = TimelineProxyMock(.init(isAutoUpdating: configuration.shouldUseAutoUpdatingTimeline,
|
||||||
timeline.sendMessageEventContentReturnValue = .success(())
|
timelineStartReached: configuration.timelineStartReached))
|
||||||
timeline.paginateBackwardsRequestSizeReturnValue = .success(())
|
|
||||||
timeline.paginateForwardsRequestSizeReturnValue = .success(())
|
|
||||||
timeline.sendReadReceiptForTypeReturnValue = .success(())
|
|
||||||
|
|
||||||
if configuration.shouldUseAutoUpdatingTimeline {
|
|
||||||
timeline.underlyingTimelineProvider = AutoUpdatingRoomTimelineProviderMock()
|
|
||||||
} else {
|
|
||||||
let timelineProvider = RoomTimelineProviderMock()
|
|
||||||
timelineProvider.paginationState = .init(backward: configuration.timelineStartReached ? .timelineEndReached : .idle, forward: .timelineEndReached)
|
|
||||||
timelineProvider.underlyingMembershipChangePublisher = PassthroughSubject().eraseToAnyPublisher()
|
|
||||||
timeline.underlyingTimelineProvider = timelineProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
self.timeline = timeline
|
|
||||||
|
|
||||||
ownUserID = configuration.ownUserID
|
ownUserID = configuration.ownUserID
|
||||||
|
|
||||||
|
35
ElementX/Sources/Mocks/TimelineProxyMock.swift
Normal file
35
ElementX/Sources/Mocks/TimelineProxyMock.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2024 New Vector Ltd.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
// Please see LICENSE in the repository root for full details.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension TimelineProxyMock {
|
||||||
|
struct Configuration {
|
||||||
|
var isAutoUpdating = false
|
||||||
|
var timelineStartReached = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
convenience init(_ configuration: Configuration) {
|
||||||
|
self.init()
|
||||||
|
|
||||||
|
sendMessageEventContentReturnValue = .success(())
|
||||||
|
paginateBackwardsRequestSizeReturnValue = .success(())
|
||||||
|
paginateForwardsRequestSizeReturnValue = .success(())
|
||||||
|
sendReadReceiptForTypeReturnValue = .success(())
|
||||||
|
|
||||||
|
if configuration.isAutoUpdating {
|
||||||
|
underlyingTimelineProvider = AutoUpdatingRoomTimelineProviderMock()
|
||||||
|
} else {
|
||||||
|
let timelineProvider = RoomTimelineProviderMock()
|
||||||
|
timelineProvider.paginationState = .init(backward: configuration.timelineStartReached ? .timelineEndReached : .idle, forward: .timelineEndReached)
|
||||||
|
timelineProvider.underlyingMembershipChangePublisher = PassthroughSubject().eraseToAnyPublisher()
|
||||||
|
underlyingTimelineProvider = timelineProvider
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -50,7 +50,7 @@ struct BigIcon: View {
|
|||||||
var style: Style = .defaultSolid
|
var style: Style = .defaultSolid
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
CompoundIcon(icon, size: .custom(32), relativeTo: .title)
|
CompoundIcon(icon, size: .custom(32), relativeTo: .compound.headingLG)
|
||||||
.modifier(BigIconModifier(style: style))
|
.modifier(BigIconModifier(style: style))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ extension Image {
|
|||||||
resizable()
|
resizable()
|
||||||
.renderingMode(.template)
|
.renderingMode(.template)
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.scaledPadding(insets, relativeTo: .title)
|
.scaledPadding(insets, relativeTo: .compound.headingLG)
|
||||||
.modifier(BigIconModifier(style: style))
|
.modifier(BigIconModifier(style: style))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ private struct BigIconModifier: ViewModifier {
|
|||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
.scaledFrame(size: 64, relativeTo: .title)
|
.scaledFrame(size: 64, relativeTo: .compound.headingLG)
|
||||||
.foregroundColor(style.foregroundColor)
|
.foregroundColor(style.foregroundColor)
|
||||||
.background {
|
.background {
|
||||||
RoundedRectangle(cornerRadius: 14)
|
RoundedRectangle(cornerRadius: 14)
|
||||||
|
@ -15,6 +15,13 @@ struct MediaUploadPreviewScreenViewState: BindableState {
|
|||||||
let url: URL
|
let url: URL
|
||||||
let title: String?
|
let title: String?
|
||||||
var shouldDisableInteraction = false
|
var shouldDisableInteraction = false
|
||||||
|
|
||||||
|
var bindings = MediaUploadPreviewScreenBindings()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MediaUploadPreviewScreenBindings: BindableState {
|
||||||
|
var caption = NSAttributedString()
|
||||||
|
var presendCallback: (() -> Void)?
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaUploadPreviewScreenViewAction {
|
enum MediaUploadPreviewScreenViewAction {
|
||||||
|
@ -42,6 +42,9 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func process(viewAction: MediaUploadPreviewScreenViewAction) {
|
override func process(viewAction: MediaUploadPreviewScreenViewAction) {
|
||||||
|
// Get the current caption before all the processing starts.
|
||||||
|
let caption = state.bindings.caption.nonBlankString
|
||||||
|
|
||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .send:
|
case .send:
|
||||||
Task {
|
Task {
|
||||||
@ -51,7 +54,7 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
|
|
||||||
switch await mediaUploadingPreprocessor.processMedia(at: url) {
|
switch await mediaUploadingPreprocessor.processMedia(at: url) {
|
||||||
case .success(let mediaInfo):
|
case .success(let mediaInfo):
|
||||||
switch await sendAttachment(mediaInfo: mediaInfo, progressSubject: progressSubject) {
|
switch await sendAttachment(mediaInfo: mediaInfo, caption: caption, progressSubject: progressSubject) {
|
||||||
case .success:
|
case .success:
|
||||||
actionsSubject.send(.dismiss)
|
actionsSubject.send(.dismiss)
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
@ -75,20 +78,38 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func sendAttachment(mediaInfo: MediaInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, TimelineProxyError> {
|
private func sendAttachment(mediaInfo: MediaInfo, caption: String?, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, TimelineProxyError> {
|
||||||
let requestHandle: ((SendAttachmentJoinHandleProtocol) -> Void) = { [weak self] handle in
|
let requestHandle: ((SendAttachmentJoinHandleProtocol) -> Void) = { [weak self] handle in
|
||||||
self?.requestHandle = handle
|
self?.requestHandle = handle
|
||||||
}
|
}
|
||||||
|
|
||||||
switch mediaInfo {
|
switch mediaInfo {
|
||||||
case let .image(imageURL, thumbnailURL, imageInfo):
|
case let .image(imageURL, thumbnailURL, imageInfo):
|
||||||
return await roomProxy.timeline.sendImage(url: imageURL, thumbnailURL: thumbnailURL, imageInfo: imageInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
return await roomProxy.timeline.sendImage(url: imageURL,
|
||||||
|
thumbnailURL: thumbnailURL,
|
||||||
|
imageInfo: imageInfo,
|
||||||
|
caption: caption,
|
||||||
|
progressSubject: progressSubject,
|
||||||
|
requestHandle: requestHandle)
|
||||||
case let .video(videoURL, thumbnailURL, videoInfo):
|
case let .video(videoURL, thumbnailURL, videoInfo):
|
||||||
return await roomProxy.timeline.sendVideo(url: videoURL, thumbnailURL: thumbnailURL, videoInfo: videoInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
return await roomProxy.timeline.sendVideo(url: videoURL,
|
||||||
|
thumbnailURL: thumbnailURL,
|
||||||
|
videoInfo: videoInfo,
|
||||||
|
caption: caption,
|
||||||
|
progressSubject: progressSubject,
|
||||||
|
requestHandle: requestHandle)
|
||||||
case let .audio(audioURL, audioInfo):
|
case let .audio(audioURL, audioInfo):
|
||||||
return await roomProxy.timeline.sendAudio(url: audioURL, audioInfo: audioInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
return await roomProxy.timeline.sendAudio(url: audioURL,
|
||||||
|
audioInfo: audioInfo,
|
||||||
|
caption: caption,
|
||||||
|
progressSubject: progressSubject,
|
||||||
|
requestHandle: requestHandle)
|
||||||
case let .file(fileURL, fileInfo):
|
case let .file(fileURL, fileInfo):
|
||||||
return await roomProxy.timeline.sendFile(url: fileURL, fileInfo: fileInfo, progressSubject: progressSubject, requestHandle: requestHandle)
|
return await roomProxy.timeline.sendFile(url: fileURL,
|
||||||
|
fileInfo: fileInfo,
|
||||||
|
caption: caption,
|
||||||
|
progressSubject: progressSubject,
|
||||||
|
requestHandle: requestHandle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,3 +139,10 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
userIndicatorController.submitIndicator(UserIndicator(title: label))
|
userIndicatorController.submitIndicator(UserIndicator(title: label))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension NSAttributedString {
|
||||||
|
var nonBlankString: String? {
|
||||||
|
guard !string.isBlank else { return nil }
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
// Please see LICENSE in the repository root for full details.
|
// Please see LICENSE in the repository root for full details.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Compound
|
||||||
import QuickLook
|
import QuickLook
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
@ -17,13 +18,20 @@ struct MediaUploadPreviewScreen: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
mainContent
|
mainContent
|
||||||
.id(UUID())
|
.id(context.viewState.url)
|
||||||
|
.ignoresSafeArea(edges: [.horizontal])
|
||||||
|
.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||||
|
composer
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
.padding(.vertical, 16)
|
||||||
|
.background() // Don't use compound so we match the QLPreviewController.
|
||||||
|
}
|
||||||
.navigationTitle(title)
|
.navigationTitle(title)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.disabled(context.viewState.shouldDisableInteraction)
|
|
||||||
.ignoresSafeArea(edges: [.horizontal, .bottom])
|
|
||||||
.toolbar { toolbar }
|
.toolbar { toolbar }
|
||||||
|
.disabled(context.viewState.shouldDisableInteraction)
|
||||||
.interactiveDismissDisabled()
|
.interactiveDismissDisabled()
|
||||||
|
.preferredColorScheme(.dark)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@ -38,18 +46,31 @@ struct MediaUploadPreviewScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var composer: some View {
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
MessageComposerTextField(placeholder: L10n.richTextEditorComposerCaptionPlaceholder,
|
||||||
|
text: $context.caption,
|
||||||
|
presendCallback: $context.presendCallback,
|
||||||
|
maxHeight: ComposerConstant.maxHeight,
|
||||||
|
keyHandler: { _ in },
|
||||||
|
pasteHandler: { _ in })
|
||||||
|
.messageComposerStyle()
|
||||||
|
|
||||||
|
SendButton {
|
||||||
|
context.send(viewAction: .send)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ToolbarContentBuilder
|
@ToolbarContentBuilder
|
||||||
private var toolbar: some ToolbarContent {
|
private var toolbar: some ToolbarContent {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
Button { context.send(viewAction: .cancel) } label: {
|
Button { context.send(viewAction: .cancel) } label: {
|
||||||
Text(L10n.actionCancel)
|
Text(L10n.actionCancel)
|
||||||
}
|
}
|
||||||
}
|
// Fix a bug with the preferredColorScheme on iOS 18 where the button doesn't
|
||||||
ToolbarItem(placement: .confirmationAction) {
|
// follow the dark colour scheme on devices running with dark mode disabled.
|
||||||
Button { context.send(viewAction: .send) } label: {
|
.tint(.compound.textActionPrimary)
|
||||||
Text(L10n.actionSend)
|
|
||||||
}
|
|
||||||
.disabled(context.viewState.shouldDisableInteraction)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,21 +132,6 @@ private class PreviewItem: NSObject, QLPreviewItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Previews
|
|
||||||
|
|
||||||
struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview {
|
|
||||||
static let viewModel = MediaUploadPreviewScreenViewModel(userIndicatorController: UserIndicatorControllerMock.default,
|
|
||||||
roomProxy: JoinedRoomProxyMock(),
|
|
||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
|
||||||
title: "some random file name",
|
|
||||||
url: URL.picturesDirectory)
|
|
||||||
static var previews: some View {
|
|
||||||
NavigationStack {
|
|
||||||
MediaUploadPreviewScreen(context: viewModel.context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PreviewViewController: QLPreviewController {
|
private class PreviewViewController: QLPreviewController {
|
||||||
override func viewWillLayoutSubviews() {
|
override func viewWillLayoutSubviews() {
|
||||||
super.viewWillLayoutSubviews()
|
super.viewWillLayoutSubviews()
|
||||||
@ -137,3 +143,21 @@ private class PreviewViewController: QLPreviewController {
|
|||||||
toolbarItems?.first?.isHidden = true
|
toolbarItems?.first?.isHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Previews
|
||||||
|
|
||||||
|
struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview {
|
||||||
|
static let snapshotURL = URL.picturesDirectory
|
||||||
|
static let testURL = Bundle.main.url(forResource: "AppIcon60x60@2x", withExtension: "png")
|
||||||
|
|
||||||
|
static let viewModel = MediaUploadPreviewScreenViewModel(userIndicatorController: UserIndicatorControllerMock.default,
|
||||||
|
roomProxy: JoinedRoomProxyMock(),
|
||||||
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
|
title: "App Icon.png",
|
||||||
|
url: snapshotURL)
|
||||||
|
static var previews: some View {
|
||||||
|
NavigationStack {
|
||||||
|
MediaUploadPreviewScreen(context: viewModel.context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -62,7 +62,7 @@ struct ComposerToolbar: View {
|
|||||||
if !context.composerFormattingEnabled {
|
if !context.composerFormattingEnabled {
|
||||||
if context.viewState.isUploading {
|
if context.viewState.isUploading {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
.scaledFrame(size: 44, relativeTo: .title)
|
.scaledFrame(size: 44, relativeTo: .compound.headingLG)
|
||||||
.padding(.leading, 3)
|
.padding(.leading, 3)
|
||||||
} else if context.viewState.showSendButton {
|
} else if context.viewState.showSendButton {
|
||||||
sendButton
|
sendButton
|
||||||
@ -119,27 +119,29 @@ struct ComposerToolbar: View {
|
|||||||
Image(Asset.Images.closeRte.name)
|
Image(Asset.Images.closeRte.name)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.scaledFrame(size: 30, relativeTo: .title)
|
.scaledFrame(size: 30, relativeTo: .compound.headingLG)
|
||||||
.scaledPadding(7, relativeTo: .title)
|
.scaledPadding(7, relativeTo: .compound.headingLG)
|
||||||
}
|
}
|
||||||
.accessibilityLabel(L10n.actionClose)
|
.accessibilityLabel(L10n.actionClose)
|
||||||
.accessibilityIdentifier(A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions)
|
.accessibilityIdentifier(A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var sendButton: some View {
|
private var sendButton: some View {
|
||||||
Button {
|
Group {
|
||||||
sendMessage()
|
if context.viewState.composerMode.isEdit {
|
||||||
} label: {
|
Button(action: sendMessage) {
|
||||||
CompoundIcon(context.viewState.composerMode.isEdit ? \.check : \.sendSolid)
|
CompoundIcon(\.check, size: .medium, relativeTo: .compound.headingLG)
|
||||||
.scaledPadding(6, relativeTo: .title)
|
.foregroundColor(.white)
|
||||||
.accessibilityLabel(context.viewState.composerMode.isEdit ? L10n.actionConfirm : L10n.actionSend)
|
.scaledPadding(6, relativeTo: .compound.headingLG)
|
||||||
.foregroundColor(context.viewState.sendButtonDisabled ? .compound.iconDisabled : .white)
|
.background(.compound.iconAccentTertiary, in: Circle())
|
||||||
.background {
|
.accessibilityLabel(L10n.actionConfirm)
|
||||||
Circle()
|
|
||||||
.foregroundColor(context.viewState.sendButtonDisabled ? .clear : .compound.iconAccentTertiary)
|
|
||||||
}
|
}
|
||||||
.scaledPadding(4, relativeTo: .title)
|
} else {
|
||||||
|
SendButton(action: sendMessage)
|
||||||
|
.accessibilityLabel(L10n.actionSend)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.scaledPadding(4, relativeTo: .compound.headingLG)
|
||||||
.disabled(context.viewState.sendButtonDisabled)
|
.disabled(context.viewState.sendButtonDisabled)
|
||||||
.animation(.linear(duration: 0.1).disabledDuringTests(), value: context.viewState.sendButtonDisabled)
|
.animation(.linear(duration: 0.1).disabledDuringTests(), value: context.viewState.sendButtonDisabled)
|
||||||
.keyboardShortcut(.return, modifiers: [.command])
|
.keyboardShortcut(.return, modifiers: [.command])
|
||||||
@ -271,8 +273,8 @@ struct ComposerToolbar: View {
|
|||||||
} label: {
|
} label: {
|
||||||
CompoundIcon(\.delete)
|
CompoundIcon(\.delete)
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
.scaledFrame(size: 30, relativeTo: .title)
|
.scaledFrame(size: 30, relativeTo: .compound.headingLG)
|
||||||
.scaledPadding(7, relativeTo: .title)
|
.scaledPadding(7, relativeTo: .compound.headingLG)
|
||||||
}
|
}
|
||||||
.buttonStyle(.compound(.plain))
|
.buttonStyle(.compound(.plain))
|
||||||
.accessibilityLabel(L10n.a11yDelete)
|
.accessibilityLabel(L10n.a11yDelete)
|
||||||
@ -291,6 +293,8 @@ struct ComposerToolbar: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Previews
|
||||||
|
|
||||||
struct ComposerToolbar_Previews: PreviewProvider, TestablePreview {
|
struct ComposerToolbar_Previews: PreviewProvider, TestablePreview {
|
||||||
static let wysiwygViewModel = WysiwygComposerViewModel()
|
static let wysiwygViewModel = WysiwygComposerViewModel()
|
||||||
static let composerViewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel,
|
static let composerViewModel = ComposerToolbarViewModel(wysiwygViewModel: wysiwygViewModel,
|
||||||
@ -330,8 +334,6 @@ struct ComposerToolbar_Previews: PreviewProvider, TestablePreview {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Mock
|
|
||||||
|
|
||||||
extension ComposerToolbar {
|
extension ComposerToolbar {
|
||||||
static func mock(focused: Bool = true) -> ComposerToolbar {
|
static func mock(focused: Bool = true) -> ComposerToolbar {
|
||||||
let wysiwygViewModel = WysiwygComposerViewModel()
|
let wysiwygViewModel = WysiwygComposerViewModel()
|
||||||
|
@ -35,17 +35,8 @@ struct MessageComposer: View {
|
|||||||
resizeGrabber
|
resizeGrabber
|
||||||
}
|
}
|
||||||
|
|
||||||
mainContent
|
composerTextField
|
||||||
.padding(.horizontal, 12.0)
|
.messageComposerStyle(header: header)
|
||||||
.clipShape(composerShape)
|
|
||||||
.background {
|
|
||||||
ZStack {
|
|
||||||
composerShape
|
|
||||||
.fill(Color.compound.bgSubtleSecondary)
|
|
||||||
composerShape
|
|
||||||
.stroke(Color.compound._borderTextFieldFocused, lineWidth: 0.5)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Explicitly disable all animations to fix weirdness with the header immediately
|
// Explicitly disable all animations to fix weirdness with the header immediately
|
||||||
// appearing whilst the text field and keyboard are still animating up to it.
|
// appearing whilst the text field and keyboard are still animating up to it.
|
||||||
.animation(.noAnimation, value: mode)
|
.animation(.noAnimation, value: mode)
|
||||||
@ -57,10 +48,8 @@ struct MessageComposer: View {
|
|||||||
|
|
||||||
@State private var composerFrame = CGRect.zero
|
@State private var composerFrame = CGRect.zero
|
||||||
|
|
||||||
private var mainContent: some View {
|
@ViewBuilder
|
||||||
VStack(alignment: .leading, spacing: -6) {
|
private var composerTextField: some View {
|
||||||
header
|
|
||||||
|
|
||||||
if composerFormattingEnabled {
|
if composerFormattingEnabled {
|
||||||
Color.clear
|
Color.clear
|
||||||
.overlay(alignment: .top) {
|
.overlay(alignment: .top) {
|
||||||
@ -70,8 +59,6 @@ struct MessageComposer: View {
|
|||||||
}
|
}
|
||||||
.frame(minHeight: ComposerConstant.minHeight, maxHeight: max(composerHeight, composerFrame.height),
|
.frame(minHeight: ComposerConstant.minHeight, maxHeight: max(composerHeight, composerFrame.height),
|
||||||
alignment: .top)
|
alignment: .top)
|
||||||
.tint(.compound.iconAccentTertiary)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
.onAppear {
|
.onAppear {
|
||||||
onAppearAction()
|
onAppearAction()
|
||||||
}
|
}
|
||||||
@ -82,9 +69,6 @@ struct MessageComposer: View {
|
|||||||
maxHeight: ComposerConstant.maxHeight,
|
maxHeight: ComposerConstant.maxHeight,
|
||||||
keyHandler: { handleKeyPress($0) },
|
keyHandler: { handleKeyPress($0) },
|
||||||
pasteHandler: pasteAction)
|
pasteHandler: pasteAction)
|
||||||
.tint(.compound.iconAccentTertiary)
|
|
||||||
.padding(.vertical, 10)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,6 +184,42 @@ private struct MessageComposerHeaderLabelStyle: LabelStyle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Style
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
func messageComposerStyle(header: some View = EmptyView()) -> some View {
|
||||||
|
modifier(MessageComposerStyleModifier(header: header))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct MessageComposerStyleModifier<Header: View>: ViewModifier {
|
||||||
|
private let composerShape = RoundedRectangle(cornerRadius: 21, style: .circular)
|
||||||
|
|
||||||
|
let header: Header
|
||||||
|
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
VStack(alignment: .leading, spacing: -6) {
|
||||||
|
header
|
||||||
|
|
||||||
|
content
|
||||||
|
.tint(.compound.iconAccentTertiary)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 12.0)
|
||||||
|
.clipShape(composerShape)
|
||||||
|
.background {
|
||||||
|
ZStack {
|
||||||
|
composerShape
|
||||||
|
.fill(Color.compound.bgSubtleSecondary)
|
||||||
|
composerShape
|
||||||
|
.stroke(Color.compound.borderInteractiveSecondary, lineWidth: 0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Previews
|
||||||
|
|
||||||
struct MessageComposer_Previews: PreviewProvider, TestablePreview {
|
struct MessageComposer_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = TimelineViewModel.mock
|
static let viewModel = TimelineViewModel.mock
|
||||||
|
|
||||||
|
@ -18,8 +18,8 @@ struct RoomAttachmentPicker: View {
|
|||||||
Menu {
|
Menu {
|
||||||
menuContent
|
menuContent
|
||||||
} label: {
|
} label: {
|
||||||
CompoundIcon(asset: Asset.Images.composerAttachment, size: .custom(30), relativeTo: .title)
|
CompoundIcon(asset: Asset.Images.composerAttachment, size: .custom(30), relativeTo: .compound.headingLG)
|
||||||
.scaledPadding(7, relativeTo: .title)
|
.scaledPadding(7, relativeTo: .compound.headingLG)
|
||||||
}
|
}
|
||||||
.buttonStyle(RoomAttachmentPickerButtonStyle())
|
.buttonStyle(RoomAttachmentPickerButtonStyle())
|
||||||
.accessibilityLabel(L10n.actionAddToTimeline)
|
.accessibilityLabel(L10n.actionAddToTimeline)
|
||||||
|
@ -32,18 +32,15 @@ struct VoiceMessageRecordingButton: View {
|
|||||||
} label: {
|
} label: {
|
||||||
switch mode {
|
switch mode {
|
||||||
case .idle:
|
case .idle:
|
||||||
CompoundIcon(\.micOn, size: .medium, relativeTo: .title)
|
CompoundIcon(\.micOn, size: .medium, relativeTo: .compound.headingLG)
|
||||||
.foregroundColor(.compound.iconSecondary)
|
.foregroundColor(.compound.iconSecondary)
|
||||||
.scaledPadding(10, relativeTo: .title)
|
.scaledPadding(10, relativeTo: .compound.headingLG)
|
||||||
case .recording:
|
case .recording:
|
||||||
CompoundIcon(asset: Asset.Images.stopRecording, size: .medium, relativeTo: .title)
|
CompoundIcon(asset: Asset.Images.stopRecording, size: .medium, relativeTo: .compound.headingLG)
|
||||||
.foregroundColor(.compound.iconOnSolidPrimary)
|
.foregroundColor(.compound.iconOnSolidPrimary)
|
||||||
.scaledPadding(6, relativeTo: .title)
|
.scaledPadding(6, relativeTo: .compound.headingLG)
|
||||||
.background(
|
.background(.compound.bgActionPrimaryRest, in: Circle())
|
||||||
Circle()
|
.scaledPadding(4, relativeTo: .compound.headingLG)
|
||||||
.foregroundColor(.compound.bgActionPrimaryRest)
|
|
||||||
)
|
|
||||||
.scaledPadding(4, relativeTo: .title)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.buttonStyle(VoiceMessageRecordingButtonStyle())
|
.buttonStyle(VoiceMessageRecordingButtonStyle())
|
||||||
|
@ -223,13 +223,14 @@ final class TimelineProxy: TimelineProxyProtocol {
|
|||||||
|
|
||||||
func sendAudio(url: URL,
|
func sendAudio(url: URL,
|
||||||
audioInfo: AudioInfo,
|
audioInfo: AudioInfo,
|
||||||
|
caption: String?,
|
||||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||||
MXLog.info("Sending audio")
|
MXLog.info("Sending audio")
|
||||||
|
|
||||||
let handle = timeline.sendAudio(url: url.path(percentEncoded: false),
|
let handle = timeline.sendAudio(url: url.path(percentEncoded: false),
|
||||||
audioInfo: audioInfo,
|
audioInfo: audioInfo,
|
||||||
caption: nil,
|
caption: caption,
|
||||||
formattedCaption: nil,
|
formattedCaption: nil,
|
||||||
progressWatcher: UploadProgressListener { progress in
|
progressWatcher: UploadProgressListener { progress in
|
||||||
progressSubject?.send(progress)
|
progressSubject?.send(progress)
|
||||||
@ -251,13 +252,14 @@ final class TimelineProxy: TimelineProxyProtocol {
|
|||||||
|
|
||||||
func sendFile(url: URL,
|
func sendFile(url: URL,
|
||||||
fileInfo: FileInfo,
|
fileInfo: FileInfo,
|
||||||
|
caption: String?,
|
||||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||||
MXLog.info("Sending file")
|
MXLog.info("Sending file")
|
||||||
|
|
||||||
let handle = timeline.sendFile(url: url.path(percentEncoded: false),
|
let handle = timeline.sendFile(url: url.path(percentEncoded: false),
|
||||||
fileInfo: fileInfo,
|
fileInfo: fileInfo,
|
||||||
caption: nil,
|
caption: caption,
|
||||||
formattedCaption: nil,
|
formattedCaption: nil,
|
||||||
progressWatcher: UploadProgressListener { progress in
|
progressWatcher: UploadProgressListener { progress in
|
||||||
progressSubject?.send(progress)
|
progressSubject?.send(progress)
|
||||||
@ -277,9 +279,11 @@ final class TimelineProxy: TimelineProxyProtocol {
|
|||||||
return .success(())
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable:next function_parameter_count
|
||||||
func sendImage(url: URL,
|
func sendImage(url: URL,
|
||||||
thumbnailURL: URL,
|
thumbnailURL: URL,
|
||||||
imageInfo: ImageInfo,
|
imageInfo: ImageInfo,
|
||||||
|
caption: String?,
|
||||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||||
MXLog.info("Sending image")
|
MXLog.info("Sending image")
|
||||||
@ -287,7 +291,7 @@ final class TimelineProxy: TimelineProxyProtocol {
|
|||||||
let handle = timeline.sendImage(url: url.path(percentEncoded: false),
|
let handle = timeline.sendImage(url: url.path(percentEncoded: false),
|
||||||
thumbnailUrl: thumbnailURL.path(percentEncoded: false),
|
thumbnailUrl: thumbnailURL.path(percentEncoded: false),
|
||||||
imageInfo: imageInfo,
|
imageInfo: imageInfo,
|
||||||
caption: nil,
|
caption: caption,
|
||||||
formattedCaption: nil,
|
formattedCaption: nil,
|
||||||
progressWatcher: UploadProgressListener { progress in
|
progressWatcher: UploadProgressListener { progress in
|
||||||
progressSubject?.send(progress)
|
progressSubject?.send(progress)
|
||||||
@ -325,9 +329,11 @@ final class TimelineProxy: TimelineProxyProtocol {
|
|||||||
return .success(())
|
return .success(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable:next function_parameter_count
|
||||||
func sendVideo(url: URL,
|
func sendVideo(url: URL,
|
||||||
thumbnailURL: URL,
|
thumbnailURL: URL,
|
||||||
videoInfo: VideoInfo,
|
videoInfo: VideoInfo,
|
||||||
|
caption: String?,
|
||||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
|
||||||
MXLog.info("Sending video")
|
MXLog.info("Sending video")
|
||||||
@ -335,7 +341,7 @@ final class TimelineProxy: TimelineProxyProtocol {
|
|||||||
let handle = timeline.sendVideo(url: url.path(percentEncoded: false),
|
let handle = timeline.sendVideo(url: url.path(percentEncoded: false),
|
||||||
thumbnailUrl: thumbnailURL.path(percentEncoded: false),
|
thumbnailUrl: thumbnailURL.path(percentEncoded: false),
|
||||||
videoInfo: videoInfo,
|
videoInfo: videoInfo,
|
||||||
caption: nil,
|
caption: caption,
|
||||||
formattedCaption: nil,
|
formattedCaption: nil,
|
||||||
progressWatcher: UploadProgressListener { progress in
|
progressWatcher: UploadProgressListener { progress in
|
||||||
progressSubject?.send(progress)
|
progressSubject?.send(progress)
|
||||||
|
@ -51,17 +51,21 @@ protocol TimelineProxyProtocol {
|
|||||||
|
|
||||||
func sendAudio(url: URL,
|
func sendAudio(url: URL,
|
||||||
audioInfo: AudioInfo,
|
audioInfo: AudioInfo,
|
||||||
|
caption: String?,
|
||||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||||
|
|
||||||
func sendFile(url: URL,
|
func sendFile(url: URL,
|
||||||
fileInfo: FileInfo,
|
fileInfo: FileInfo,
|
||||||
|
caption: String?,
|
||||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||||
|
|
||||||
|
// swiftlint:disable:next function_parameter_count
|
||||||
func sendImage(url: URL,
|
func sendImage(url: URL,
|
||||||
thumbnailURL: URL,
|
thumbnailURL: URL,
|
||||||
imageInfo: ImageInfo,
|
imageInfo: ImageInfo,
|
||||||
|
caption: String?,
|
||||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||||
|
|
||||||
@ -71,9 +75,11 @@ protocol TimelineProxyProtocol {
|
|||||||
zoomLevel: UInt8?,
|
zoomLevel: UInt8?,
|
||||||
assetType: AssetType?) async -> Result<Void, TimelineProxyError>
|
assetType: AssetType?) async -> Result<Void, TimelineProxyError>
|
||||||
|
|
||||||
|
// swiftlint:disable:next function_parameter_count
|
||||||
func sendVideo(url: URL,
|
func sendVideo(url: URL,
|
||||||
thumbnailURL: URL,
|
thumbnailURL: URL,
|
||||||
videoInfo: VideoInfo,
|
videoInfo: VideoInfo,
|
||||||
|
caption: String?,
|
||||||
progressSubject: CurrentValueSubject<Double, Never>?,
|
progressSubject: CurrentValueSubject<Double, Never>?,
|
||||||
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError>
|
||||||
|
|
||||||
|
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-en-GB.Reply.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-en-GB.Reply.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-en-GB.Voice-Message.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-en-GB.Voice-Message.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-en-GB.With-Suggestions.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-en-GB.With-Suggestions.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-pseudo.Reply.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-pseudo.Reply.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-pseudo.Voice-Message.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-pseudo.Voice-Message.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-pseudo.With-Suggestions.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPad-pseudo.With-Suggestions.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-en-GB.Reply.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-en-GB.Reply.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-en-GB.Voice-Message.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-en-GB.Voice-Message.png
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-pseudo.Reply.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-pseudo.Reply.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-pseudo.Voice-Message.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_composerToolbar-iPhone-16-pseudo.Voice-Message.png
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaUploadPreviewScreen-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaUploadPreviewScreen-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaUploadPreviewScreen-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaUploadPreviewScreen-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaUploadPreviewScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaUploadPreviewScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaUploadPreviewScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_mediaUploadPreviewScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying-in-thread.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying-in-thread.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-en-GB.Replying.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying-in-thread.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying-in-thread.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPad-pseudo.Replying.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-en-GB.Replying.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageComposer-iPhone-16-pseudo.Replying.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_timelineView-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
@ -10,4 +10,135 @@ import XCTest
|
|||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class MediaUploadPreviewScreenViewModelTests: XCTestCase { }
|
class MediaUploadPreviewScreenViewModelTests: XCTestCase {
|
||||||
|
var timelineProxy: TimelineProxyMock!
|
||||||
|
var viewModel: MediaUploadPreviewScreenViewModel!
|
||||||
|
var context: MediaUploadPreviewScreenViewModel.Context { viewModel.context }
|
||||||
|
|
||||||
|
enum TestError: Swift.Error {
|
||||||
|
case unexpectedParameter
|
||||||
|
case unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
AppSettings.resetAllSettings()
|
||||||
|
let appSettings = AppSettings()
|
||||||
|
appSettings.optimizeMediaUploads = false
|
||||||
|
ServiceLocator.shared.register(appSettings: appSettings)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
AppSettings.resetAllSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImageUploadWithoutCaption() async throws {
|
||||||
|
setUpViewModel(url: imageURL, expectedCaption: nil)
|
||||||
|
context.caption = .init("")
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImageUploadWithBlankCaption() async throws {
|
||||||
|
setUpViewModel(url: imageURL, expectedCaption: nil)
|
||||||
|
context.caption = .init(" ")
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImageUploadWithCaption() async throws {
|
||||||
|
let caption = "This is a really great image!"
|
||||||
|
setUpViewModel(url: imageURL, expectedCaption: caption)
|
||||||
|
context.caption = .init(string: caption)
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVideoUploadWithoutCaption() async throws {
|
||||||
|
setUpViewModel(url: videoURL, expectedCaption: nil)
|
||||||
|
context.caption = .init("")
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVideoUploadWithCaption() async throws {
|
||||||
|
let caption = "Check out this video!"
|
||||||
|
setUpViewModel(url: videoURL, expectedCaption: caption)
|
||||||
|
context.caption = .init(string: caption)
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAudioUploadWithoutCaption() async throws {
|
||||||
|
setUpViewModel(url: audioURL, expectedCaption: nil)
|
||||||
|
context.caption = .init("")
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAudioUploadWithCaption() async throws {
|
||||||
|
let caption = "Listen to this!"
|
||||||
|
setUpViewModel(url: audioURL, expectedCaption: caption)
|
||||||
|
context.caption = .init(string: caption)
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFileUploadWithoutCaption() async throws {
|
||||||
|
setUpViewModel(url: fileURL, expectedCaption: nil)
|
||||||
|
context.caption = .init("")
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFileUploadWithCaption() async throws {
|
||||||
|
let caption = "Please will you check my article."
|
||||||
|
setUpViewModel(url: fileURL, expectedCaption: caption)
|
||||||
|
context.caption = .init(string: caption)
|
||||||
|
try await send()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
private var audioURL: URL { assertResourceURL(filename: "test_audio.mp3") }
|
||||||
|
private var fileURL: URL { assertResourceURL(filename: "test_pdf.pdf") }
|
||||||
|
private var imageURL: URL { assertResourceURL(filename: "test_animated_image.gif") }
|
||||||
|
private var videoURL: URL { assertResourceURL(filename: "landscape_test_video.mov") }
|
||||||
|
|
||||||
|
private func assertResourceURL(filename: String) -> URL {
|
||||||
|
guard let url = Bundle(for: Self.self).url(forResource: filename, withExtension: nil) else {
|
||||||
|
XCTFail("Failed retrieving test asset")
|
||||||
|
return .picturesDirectory
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setUpViewModel(url: URL, expectedCaption: String?) {
|
||||||
|
timelineProxy = TimelineProxyMock(.init())
|
||||||
|
timelineProxy.sendAudioUrlAudioInfoCaptionProgressSubjectRequestHandleClosure = { [weak self] _, _, caption, _, _ in
|
||||||
|
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||||
|
}
|
||||||
|
timelineProxy.sendFileUrlFileInfoCaptionProgressSubjectRequestHandleClosure = { [weak self] _, _, caption, _, _ in
|
||||||
|
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||||
|
}
|
||||||
|
timelineProxy.sendImageUrlThumbnailURLImageInfoCaptionProgressSubjectRequestHandleClosure = { [weak self] _, _, _, caption, _, _ in
|
||||||
|
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||||
|
}
|
||||||
|
timelineProxy.sendVideoUrlThumbnailURLVideoInfoCaptionProgressSubjectRequestHandleClosure = { [weak self] _, _, _, caption, _, _ in
|
||||||
|
self?.verifyCaption(caption, expectedCaption: expectedCaption) ?? .failure(.sdkError(TestError.unknown))
|
||||||
|
}
|
||||||
|
|
||||||
|
let roomProxy = JoinedRoomProxyMock(.init())
|
||||||
|
roomProxy.timeline = timelineProxy
|
||||||
|
viewModel = MediaUploadPreviewScreenViewModel(userIndicatorController: UserIndicatorControllerMock(),
|
||||||
|
roomProxy: roomProxy,
|
||||||
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
|
title: "Some File",
|
||||||
|
url: url)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func verifyCaption(_ caption: String?, expectedCaption: String?) -> Result<Void, TimelineProxyError> {
|
||||||
|
guard caption == expectedCaption else {
|
||||||
|
XCTFail("The sent caption '\(caption ?? "nil")' does not match the expected value '\(expectedCaption ?? "nil")'").self
|
||||||
|
return .failure(.sdkError(TestError.unexpectedParameter))
|
||||||
|
}
|
||||||
|
return .success(())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func send() async throws {
|
||||||
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .dismiss }
|
||||||
|
context.send(viewAction: .send)
|
||||||
|
try await deferred.fulfill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user