Add support for media uploading progress

- replace the ProgressTracker with a combination of currentValueSubjects and currentValuePublishers
This commit is contained in:
Stefan Ceriu
2023-06-21 12:55:49 +03:00
committed by Stefan Ceriu
parent 9cf2aba16d
commit 8b11a0a5ec
13 changed files with 130 additions and 150 deletions
-4
View File
@@ -663,7 +663,6 @@
F508683B76EF7B23BB2CBD6D /* TimelineItemPlainStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94BCC8A9C73C1F838122C645 /* TimelineItemPlainStylerView.swift */; }; F508683B76EF7B23BB2CBD6D /* TimelineItemPlainStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94BCC8A9C73C1F838122C645 /* TimelineItemPlainStylerView.swift */; };
F519DE17A3A0F760307B2E6D /* InviteUsersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D155E09BF961BBA8F85263 /* InviteUsersScreenViewModel.swift */; }; F519DE17A3A0F760307B2E6D /* InviteUsersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D155E09BF961BBA8F85263 /* InviteUsersScreenViewModel.swift */; };
F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */; }; F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */; };
F587A9AF25A262DE5A7B0369 /* ProgressTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F28551E81CE3700E5F1EC9B5 /* ProgressTracker.swift */; };
F5D2270B5021D521C0D22E11 /* FlowCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */; }; F5D2270B5021D521C0D22E11 /* FlowCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */; };
F656F92A63D3DC1978D79427 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 07FEEEDB11543A7DED420F04 /* Compound */; }; F656F92A63D3DC1978D79427 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 07FEEEDB11543A7DED420F04 /* Compound */; };
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; }; F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
@@ -1337,7 +1336,6 @@
F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = "<group>"; }; F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = "<group>"; };
F1964EE08550BEDBD0B0F5FD /* FilePreviewScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewScreenViewModel.swift; sourceTree = "<group>"; }; F1964EE08550BEDBD0B0F5FD /* FilePreviewScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewScreenViewModel.swift; sourceTree = "<group>"; };
F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineItem.swift; sourceTree = "<group>"; }; F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineItem.swift; sourceTree = "<group>"; };
F28551E81CE3700E5F1EC9B5 /* ProgressTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressTracker.swift; sourceTree = "<group>"; };
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = "<group>"; }; F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = "<group>"; };
F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = "<group>"; }; F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = "<group>"; };
F36C0A6D59717193F49EA986 /* UserSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionTests.swift; sourceTree = "<group>"; }; F36C0A6D59717193F49EA986 /* UserSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionTests.swift; sourceTree = "<group>"; };
@@ -2983,7 +2981,6 @@
6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */, 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */,
C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */, C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */,
F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */, F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */,
F28551E81CE3700E5F1EC9B5 /* ProgressTracker.swift */,
53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */, 53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */,
DBA8DC95C079805B0B56E8A9 /* SharedUserDefaultsKeys.swift */, DBA8DC95C079805B0B56E8A9 /* SharedUserDefaultsKeys.swift */,
BB3073CCD77D906B330BC1D6 /* Tests.swift */, BB3073CCD77D906B330BC1D6 /* Tests.swift */,
@@ -4153,7 +4150,6 @@
962A4F8AD6312804E2C6BB6E /* PhotoLibraryPicker.swift in Sources */, 962A4F8AD6312804E2C6BB6E /* PhotoLibraryPicker.swift in Sources */,
9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */, 9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */,
DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */, DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */,
F587A9AF25A262DE5A7B0369 /* ProgressTracker.swift in Sources */,
2835FD52F3F618D07F799B3D /* Publisher.swift in Sources */, 2835FD52F3F618D07F799B3D /* Publisher.swift in Sources */,
743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */, 743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */,
8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */, 8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */,
@@ -177,12 +177,12 @@ class BugReportServiceMock: BugReportServiceProtocol {
var submitBugReportProgressListenerCalled: Bool { var submitBugReportProgressListenerCalled: Bool {
return submitBugReportProgressListenerCallsCount > 0 return submitBugReportProgressListenerCallsCount > 0
} }
var submitBugReportProgressListenerReceivedArguments: (bugReport: BugReport, progressListener: ProgressListener?)? var submitBugReportProgressListenerReceivedArguments: (bugReport: BugReport, progressListener: CurrentValueSubject<Double, Never>)?
var submitBugReportProgressListenerReceivedInvocations: [(bugReport: BugReport, progressListener: ProgressListener?)] = [] var submitBugReportProgressListenerReceivedInvocations: [(bugReport: BugReport, progressListener: CurrentValueSubject<Double, Never>)] = []
var submitBugReportProgressListenerReturnValue: Result<SubmitBugReportResponse, BugReportServiceError>! var submitBugReportProgressListenerReturnValue: Result<SubmitBugReportResponse, BugReportServiceError>!
var submitBugReportProgressListenerClosure: ((BugReport, ProgressListener?) async -> Result<SubmitBugReportResponse, BugReportServiceError>)? var submitBugReportProgressListenerClosure: ((BugReport, CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError>)?
func submitBugReport(_ bugReport: BugReport, progressListener: ProgressListener?) async -> Result<SubmitBugReportResponse, BugReportServiceError> { func submitBugReport(_ bugReport: BugReport, progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError> {
submitBugReportProgressListenerCallsCount += 1 submitBugReportProgressListenerCallsCount += 1
submitBugReportProgressListenerReceivedArguments = (bugReport: bugReport, progressListener: progressListener) submitBugReportProgressListenerReceivedArguments = (bugReport: bugReport, progressListener: progressListener)
submitBugReportProgressListenerReceivedInvocations.append((bugReport: bugReport, progressListener: progressListener)) submitBugReportProgressListenerReceivedInvocations.append((bugReport: bugReport, progressListener: progressListener))
@@ -633,86 +633,86 @@ class RoomProxyMock: RoomProxyProtocol {
} }
//MARK: - sendImage //MARK: - sendImage
var sendImageUrlThumbnailURLImageInfoCallsCount = 0 var sendImageUrlThumbnailURLImageInfoProgressSubjectCallsCount = 0
var sendImageUrlThumbnailURLImageInfoCalled: Bool { var sendImageUrlThumbnailURLImageInfoProgressSubjectCalled: Bool {
return sendImageUrlThumbnailURLImageInfoCallsCount > 0 return sendImageUrlThumbnailURLImageInfoProgressSubjectCallsCount > 0
} }
var sendImageUrlThumbnailURLImageInfoReceivedArguments: (url: URL, thumbnailURL: URL, imageInfo: ImageInfo)? var sendImageUrlThumbnailURLImageInfoProgressSubjectReceivedArguments: (url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?)?
var sendImageUrlThumbnailURLImageInfoReceivedInvocations: [(url: URL, thumbnailURL: URL, imageInfo: ImageInfo)] = [] var sendImageUrlThumbnailURLImageInfoProgressSubjectReceivedInvocations: [(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?)] = []
var sendImageUrlThumbnailURLImageInfoReturnValue: Result<Void, RoomProxyError>! var sendImageUrlThumbnailURLImageInfoProgressSubjectReturnValue: Result<Void, RoomProxyError>!
var sendImageUrlThumbnailURLImageInfoClosure: ((URL, URL, ImageInfo) async -> Result<Void, RoomProxyError>)? var sendImageUrlThumbnailURLImageInfoProgressSubjectClosure: ((URL, URL, ImageInfo, CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>)?
func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo) async -> Result<Void, RoomProxyError> { func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendImageUrlThumbnailURLImageInfoCallsCount += 1 sendImageUrlThumbnailURLImageInfoProgressSubjectCallsCount += 1
sendImageUrlThumbnailURLImageInfoReceivedArguments = (url: url, thumbnailURL: thumbnailURL, imageInfo: imageInfo) sendImageUrlThumbnailURLImageInfoProgressSubjectReceivedArguments = (url: url, thumbnailURL: thumbnailURL, imageInfo: imageInfo, progressSubject: progressSubject)
sendImageUrlThumbnailURLImageInfoReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, imageInfo: imageInfo)) sendImageUrlThumbnailURLImageInfoProgressSubjectReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, imageInfo: imageInfo, progressSubject: progressSubject))
if let sendImageUrlThumbnailURLImageInfoClosure = sendImageUrlThumbnailURLImageInfoClosure { if let sendImageUrlThumbnailURLImageInfoProgressSubjectClosure = sendImageUrlThumbnailURLImageInfoProgressSubjectClosure {
return await sendImageUrlThumbnailURLImageInfoClosure(url, thumbnailURL, imageInfo) return await sendImageUrlThumbnailURLImageInfoProgressSubjectClosure(url, thumbnailURL, imageInfo, progressSubject)
} else { } else {
return sendImageUrlThumbnailURLImageInfoReturnValue return sendImageUrlThumbnailURLImageInfoProgressSubjectReturnValue
} }
} }
//MARK: - sendVideo //MARK: - sendVideo
var sendVideoUrlThumbnailURLVideoInfoCallsCount = 0 var sendVideoUrlThumbnailURLVideoInfoProgressSubjectCallsCount = 0
var sendVideoUrlThumbnailURLVideoInfoCalled: Bool { var sendVideoUrlThumbnailURLVideoInfoProgressSubjectCalled: Bool {
return sendVideoUrlThumbnailURLVideoInfoCallsCount > 0 return sendVideoUrlThumbnailURLVideoInfoProgressSubjectCallsCount > 0
} }
var sendVideoUrlThumbnailURLVideoInfoReceivedArguments: (url: URL, thumbnailURL: URL, videoInfo: VideoInfo)? var sendVideoUrlThumbnailURLVideoInfoProgressSubjectReceivedArguments: (url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?)?
var sendVideoUrlThumbnailURLVideoInfoReceivedInvocations: [(url: URL, thumbnailURL: URL, videoInfo: VideoInfo)] = [] var sendVideoUrlThumbnailURLVideoInfoProgressSubjectReceivedInvocations: [(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?)] = []
var sendVideoUrlThumbnailURLVideoInfoReturnValue: Result<Void, RoomProxyError>! var sendVideoUrlThumbnailURLVideoInfoProgressSubjectReturnValue: Result<Void, RoomProxyError>!
var sendVideoUrlThumbnailURLVideoInfoClosure: ((URL, URL, VideoInfo) async -> Result<Void, RoomProxyError>)? var sendVideoUrlThumbnailURLVideoInfoProgressSubjectClosure: ((URL, URL, VideoInfo, CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>)?
func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo) async -> Result<Void, RoomProxyError> { func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendVideoUrlThumbnailURLVideoInfoCallsCount += 1 sendVideoUrlThumbnailURLVideoInfoProgressSubjectCallsCount += 1
sendVideoUrlThumbnailURLVideoInfoReceivedArguments = (url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo) sendVideoUrlThumbnailURLVideoInfoProgressSubjectReceivedArguments = (url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo, progressSubject: progressSubject)
sendVideoUrlThumbnailURLVideoInfoReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo)) sendVideoUrlThumbnailURLVideoInfoProgressSubjectReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo, progressSubject: progressSubject))
if let sendVideoUrlThumbnailURLVideoInfoClosure = sendVideoUrlThumbnailURLVideoInfoClosure { if let sendVideoUrlThumbnailURLVideoInfoProgressSubjectClosure = sendVideoUrlThumbnailURLVideoInfoProgressSubjectClosure {
return await sendVideoUrlThumbnailURLVideoInfoClosure(url, thumbnailURL, videoInfo) return await sendVideoUrlThumbnailURLVideoInfoProgressSubjectClosure(url, thumbnailURL, videoInfo, progressSubject)
} else { } else {
return sendVideoUrlThumbnailURLVideoInfoReturnValue return sendVideoUrlThumbnailURLVideoInfoProgressSubjectReturnValue
} }
} }
//MARK: - sendAudio //MARK: - sendAudio
var sendAudioUrlAudioInfoCallsCount = 0 var sendAudioUrlAudioInfoProgressSubjectCallsCount = 0
var sendAudioUrlAudioInfoCalled: Bool { var sendAudioUrlAudioInfoProgressSubjectCalled: Bool {
return sendAudioUrlAudioInfoCallsCount > 0 return sendAudioUrlAudioInfoProgressSubjectCallsCount > 0
} }
var sendAudioUrlAudioInfoReceivedArguments: (url: URL, audioInfo: AudioInfo)? var sendAudioUrlAudioInfoProgressSubjectReceivedArguments: (url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?)?
var sendAudioUrlAudioInfoReceivedInvocations: [(url: URL, audioInfo: AudioInfo)] = [] var sendAudioUrlAudioInfoProgressSubjectReceivedInvocations: [(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?)] = []
var sendAudioUrlAudioInfoReturnValue: Result<Void, RoomProxyError>! var sendAudioUrlAudioInfoProgressSubjectReturnValue: Result<Void, RoomProxyError>!
var sendAudioUrlAudioInfoClosure: ((URL, AudioInfo) async -> Result<Void, RoomProxyError>)? var sendAudioUrlAudioInfoProgressSubjectClosure: ((URL, AudioInfo, CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>)?
func sendAudio(url: URL, audioInfo: AudioInfo) async -> Result<Void, RoomProxyError> { func sendAudio(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendAudioUrlAudioInfoCallsCount += 1 sendAudioUrlAudioInfoProgressSubjectCallsCount += 1
sendAudioUrlAudioInfoReceivedArguments = (url: url, audioInfo: audioInfo) sendAudioUrlAudioInfoProgressSubjectReceivedArguments = (url: url, audioInfo: audioInfo, progressSubject: progressSubject)
sendAudioUrlAudioInfoReceivedInvocations.append((url: url, audioInfo: audioInfo)) sendAudioUrlAudioInfoProgressSubjectReceivedInvocations.append((url: url, audioInfo: audioInfo, progressSubject: progressSubject))
if let sendAudioUrlAudioInfoClosure = sendAudioUrlAudioInfoClosure { if let sendAudioUrlAudioInfoProgressSubjectClosure = sendAudioUrlAudioInfoProgressSubjectClosure {
return await sendAudioUrlAudioInfoClosure(url, audioInfo) return await sendAudioUrlAudioInfoProgressSubjectClosure(url, audioInfo, progressSubject)
} else { } else {
return sendAudioUrlAudioInfoReturnValue return sendAudioUrlAudioInfoProgressSubjectReturnValue
} }
} }
//MARK: - sendFile //MARK: - sendFile
var sendFileUrlFileInfoCallsCount = 0 var sendFileUrlFileInfoProgressSubjectCallsCount = 0
var sendFileUrlFileInfoCalled: Bool { var sendFileUrlFileInfoProgressSubjectCalled: Bool {
return sendFileUrlFileInfoCallsCount > 0 return sendFileUrlFileInfoProgressSubjectCallsCount > 0
} }
var sendFileUrlFileInfoReceivedArguments: (url: URL, fileInfo: FileInfo)? var sendFileUrlFileInfoProgressSubjectReceivedArguments: (url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?)?
var sendFileUrlFileInfoReceivedInvocations: [(url: URL, fileInfo: FileInfo)] = [] var sendFileUrlFileInfoProgressSubjectReceivedInvocations: [(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?)] = []
var sendFileUrlFileInfoReturnValue: Result<Void, RoomProxyError>! var sendFileUrlFileInfoProgressSubjectReturnValue: Result<Void, RoomProxyError>!
var sendFileUrlFileInfoClosure: ((URL, FileInfo) async -> Result<Void, RoomProxyError>)? var sendFileUrlFileInfoProgressSubjectClosure: ((URL, FileInfo, CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>)?
func sendFile(url: URL, fileInfo: FileInfo) async -> Result<Void, RoomProxyError> { func sendFile(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendFileUrlFileInfoCallsCount += 1 sendFileUrlFileInfoProgressSubjectCallsCount += 1
sendFileUrlFileInfoReceivedArguments = (url: url, fileInfo: fileInfo) sendFileUrlFileInfoProgressSubjectReceivedArguments = (url: url, fileInfo: fileInfo, progressSubject: progressSubject)
sendFileUrlFileInfoReceivedInvocations.append((url: url, fileInfo: fileInfo)) sendFileUrlFileInfoProgressSubjectReceivedInvocations.append((url: url, fileInfo: fileInfo, progressSubject: progressSubject))
if let sendFileUrlFileInfoClosure = sendFileUrlFileInfoClosure { if let sendFileUrlFileInfoProgressSubjectClosure = sendFileUrlFileInfoProgressSubjectClosure {
return await sendFileUrlFileInfoClosure(url, fileInfo) return await sendFileUrlFileInfoProgressSubjectClosure(url, fileInfo, progressSubject)
} else { } else {
return sendFileUrlFileInfoReturnValue return sendFileUrlFileInfoProgressSubjectReturnValue
} }
} }
//MARK: - retrySend //MARK: - retrySend
@@ -1,39 +0,0 @@
//
// Copyright 2023 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Combine
import Foundation
protocol ProgressListener {
var progressSubject: CurrentValueSubject<Double, Never> { get }
}
protocol ProgressPublisher: AnyObject {
var publisher: AnyPublisher<Double, Never> { get }
}
final class ProgressTracker: ProgressListener, ProgressPublisher {
let progressSubject: CurrentValueSubject<Double, Never>
var publisher: AnyPublisher<Double, Never> {
progressSubject
.eraseToAnyPublisher()
}
init(initialValue: Double = 0.0) {
progressSubject = .init(initialValue)
}
}
@@ -30,13 +30,13 @@ struct UserIndicator: Equatable, Identifiable {
static func == (lhs: UserIndicator.Progress, rhs: UserIndicator.Progress) -> Bool { static func == (lhs: UserIndicator.Progress, rhs: UserIndicator.Progress) -> Bool {
switch (lhs, rhs) { switch (lhs, rhs) {
case (.indeterminate, .indeterminate): return true case (.indeterminate, .indeterminate): return true
case (.published(let lhsPublisher), .published(let rhsPublisher)): return lhsPublisher === rhsPublisher case (.published(let lhsPublisher), .published(let rhsPublisher)): return lhsPublisher.value == rhsPublisher.value
default: return false default: return false
} }
} }
case indeterminate case indeterminate
case published(ProgressPublisher) case published(CurrentValuePublisher<Double, Never>)
} }
var id: String = UUID().uuidString var id: String = UUID().uuidString
@@ -54,14 +54,14 @@ struct UserIndicator: Equatable, Identifiable {
} }
} }
var progressPublisher: AnyPublisher<Double, Never> { var progressPublisher: CurrentValuePublisher<Double, Never> {
switch type { switch type {
case .toast(let progress), .modal(let progress, _): case .toast(let progress), .modal(let progress, _):
switch progress { switch progress {
case .none, .indeterminate: case .none, .indeterminate:
return Empty().eraseToAnyPublisher() return CurrentValueSubject<Double, Never>(0.0).asCurrentValuePublisher()
case .some(.published(let progress)): case .some(.published(let publisher)):
return progress.publisher.eraseToAnyPublisher() return publisher
} }
} }
} }
@@ -69,7 +69,7 @@ struct UserIndicatorModalView_Previews: PreviewProvider {
) )
.previewDisplayName("Spinner") .previewDisplayName("Spinner")
UserIndicatorModalView(indicator: UserIndicator(type: .modal(progress: .published(ProgressTracker(initialValue: 0.5)), UserIndicatorModalView(indicator: UserIndicator(type: .modal(progress: .published(CurrentValueSubject<Double, Never>(0.5).asCurrentValuePublisher()),
interactiveDismissDisabled: false), interactiveDismissDisabled: false),
title: "Successfully logged in", title: "Successfully logged in",
iconName: "checkmark") iconName: "checkmark")
@@ -60,8 +60,8 @@ final class BugReportScreenCoordinator: CoordinatorProtocol {
switch result { switch result {
case .cancel: case .cancel:
self.completion?(.cancel) self.completion?(.cancel)
case let .submitStarted(progressTracker): case let .submitStarted(progressPublisher):
self.startLoading(label: L10n.commonSending, progressPublisher: progressTracker) self.startLoading(label: L10n.commonSending, progressPublisher: progressPublisher)
case .submitFinished: case .submitFinished:
self.stopLoading() self.stopLoading()
self.completion?(.finish) self.completion?(.finish)
@@ -85,7 +85,7 @@ final class BugReportScreenCoordinator: CoordinatorProtocol {
private static let loadingIndicatorIdentifier = "BugReportLoading" private static let loadingIndicatorIdentifier = "BugReportLoading"
private func startLoading(label: String = L10n.commonLoading, progressPublisher: ProgressPublisher) { private func startLoading(label: String = L10n.commonLoading, progressPublisher: CurrentValuePublisher<Double, Never>) {
parameters.userIndicatorController?.submitIndicator( parameters.userIndicatorController?.submitIndicator(
UserIndicator(id: Self.loadingIndicatorIdentifier, UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal(progress: .published(progressPublisher), interactiveDismissDisabled: false), type: .modal(progress: .published(progressPublisher), interactiveDismissDisabled: false),
@@ -19,7 +19,7 @@ import UIKit
enum BugReportScreenViewModelAction { enum BugReportScreenViewModelAction {
case cancel case cancel
case submitStarted(progressTracker: ProgressTracker) case submitStarted(progressPublisher: CurrentValuePublisher<Double, Never>)
case submitFinished case submitFinished
case submitFailed(error: Error) case submitFailed(error: Error)
} }
@@ -62,8 +62,9 @@ class BugReportScreenViewModel: BugReportScreenViewModelType, BugReportScreenVie
// MARK: Private // MARK: Private
private func submitBugReport() async { private func submitBugReport() async {
let progressTracker = ProgressTracker() let progressSubject = CurrentValueSubject<Double, Never>(0.0)
actionsSubject.send(.submitStarted(progressTracker: progressTracker))
actionsSubject.send(.submitStarted(progressPublisher: progressSubject.asCurrentValuePublisher()))
var files: [URL] = [] var files: [URL] = []
if let screenshot = context.viewState.screenshot, if let screenshot = context.viewState.screenshot,
@@ -87,7 +88,7 @@ class BugReportScreenViewModel: BugReportScreenViewModelType, BugReportScreenVie
files: files) files: files)
switch await bugReportService.submitBugReport(bugReport, switch await bugReportService.submitBugReport(bugReport,
progressListener: progressTracker) { progressListener: progressSubject) {
case .success(let response): case .success(let response):
MXLog.info("Submission uploaded to: \(response.reportUrl)") MXLog.info("Submission uploaded to: \(response.reportUrl)")
actionsSubject.send(.submitFinished) actionsSubject.send(.submitFinished)
@@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Combine
import SwiftUI import SwiftUI
typealias MediaUploadPreviewScreenViewModelType = StateStoreViewModel<MediaUploadPreviewScreenViewState, MediaUploadPreviewScreenViewAction> typealias MediaUploadPreviewScreenViewModelType = StateStoreViewModel<MediaUploadPreviewScreenViewState, MediaUploadPreviewScreenViewAction>
@@ -43,11 +44,13 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
switch viewAction { switch viewAction {
case .send: case .send:
Task { Task {
startLoading() let progressSubject = CurrentValueSubject<Double, Never>(0.0)
startLoading(progressPublisher: progressSubject.asCurrentValuePublisher())
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) { switch await sendAttachment(mediaInfo: mediaInfo, progressSubject: progressSubject) {
case .success: case .success:
callback?(.dismiss) callback?(.dismiss)
case .failure(let error): case .failure(let error):
@@ -70,26 +73,26 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
// MARK: - Private // MARK: - Private
private func sendAttachment(mediaInfo: MediaInfo) async -> Result<Void, RoomProxyError> { private func sendAttachment(mediaInfo: MediaInfo, progressSubject: CurrentValueSubject<Double, Never>) async -> Result<Void, RoomProxyError> {
switch mediaInfo { switch mediaInfo {
case let .image(imageURL, thumbnailURL, imageInfo): case let .image(imageURL, thumbnailURL, imageInfo):
return await roomProxy.sendImage(url: imageURL, thumbnailURL: thumbnailURL, imageInfo: imageInfo) return await roomProxy.sendImage(url: imageURL, thumbnailURL: thumbnailURL, imageInfo: imageInfo, progressSubject: progressSubject)
case let .video(videoURL, thumbnailURL, videoInfo): case let .video(videoURL, thumbnailURL, videoInfo):
return await roomProxy.sendVideo(url: videoURL, thumbnailURL: thumbnailURL, videoInfo: videoInfo) return await roomProxy.sendVideo(url: videoURL, thumbnailURL: thumbnailURL, videoInfo: videoInfo, progressSubject: progressSubject)
case let .audio(audioURL, audioInfo): case let .audio(audioURL, audioInfo):
return await roomProxy.sendAudio(url: audioURL, audioInfo: audioInfo) return await roomProxy.sendAudio(url: audioURL, audioInfo: audioInfo, progressSubject: progressSubject)
case let .file(fileURL, fileInfo): case let .file(fileURL, fileInfo):
return await roomProxy.sendFile(url: fileURL, fileInfo: fileInfo) return await roomProxy.sendFile(url: fileURL, fileInfo: fileInfo, progressSubject: progressSubject)
} }
} }
private static let loadingIndicatorIdentifier = "MediaUploadPreviewLoading" private static let loadingIndicatorIdentifier = "MediaUploadPreviewLoading"
private func startLoading() { private func startLoading(progressPublisher: CurrentValuePublisher<Double, Never>) {
userIndicatorController?.submitIndicator( userIndicatorController?.submitIndicator(
UserIndicator(id: Self.loadingIndicatorIdentifier, UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal, type: .modal(progress: .published(progressPublisher), interactiveDismissDisabled: false),
title: L10n.commonLoading, title: L10n.commonSending,
persistent: true) persistent: true)
) )
} }
@@ -100,7 +100,7 @@ class BugReportService: NSObject, BugReportServiceProtocol {
// swiftlint:disable:next function_body_length cyclomatic_complexity // swiftlint:disable:next function_body_length cyclomatic_complexity
func submitBugReport(_ bugReport: BugReport, func submitBugReport(_ bugReport: BugReport,
progressListener: ProgressListener?) async -> Result<SubmitBugReportResponse, BugReportServiceError> { progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError> {
var params = [ var params = [
MultipartFormData(key: "user_id", type: .text(value: bugReport.userID)), MultipartFormData(key: "user_id", type: .text(value: bugReport.userID)),
MultipartFormData(key: "text", type: .text(value: bugReport.text)) MultipartFormData(key: "text", type: .text(value: bugReport.text))
@@ -148,17 +148,13 @@ class BugReportService: NSObject, BugReportServiceProtocol {
request.httpMethod = "POST" request.httpMethod = "POST"
request.httpBody = body as Data request.httpBody = body as Data
var delegate: URLSessionTaskDelegate? progressSubject
if let progressListener { .receive(on: DispatchQueue.main)
progressSubject .weakAssign(to: \.value, on: progressListener)
.receive(on: DispatchQueue.main) .store(in: &cancellables)
.weakAssign(to: \.value, on: progressListener.progressSubject)
.store(in: &cancellables)
delegate = self
}
do { do {
let (data, response) = try await session.dataWithRetry(for: request, delegate: delegate) let (data, response) = try await session.dataWithRetry(for: request, delegate: self)
guard let httpResponse = response as? HTTPURLResponse else { guard let httpResponse = response as? HTTPURLResponse else {
let errorDescription = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Unknown" let errorDescription = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Unknown"
@@ -14,6 +14,7 @@
// limitations under the License. // limitations under the License.
// //
import Combine
import Foundation import Foundation
import UIKit import UIKit
@@ -63,5 +64,5 @@ protocol BugReportServiceProtocol {
func crash() func crash()
func submitBugReport(_ bugReport: BugReport, func submitBugReport(_ bugReport: BugReport,
progressListener: ProgressListener?) async -> Result<SubmitBugReportResponse, BugReportServiceError> progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError>
} }
+30 -8
View File
@@ -276,7 +276,7 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo) async -> Result<Void, RoomProxyError> { func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer { defer {
sendMessageBackgroundTask?.stop() sendMessageBackgroundTask?.stop()
@@ -284,7 +284,9 @@ class RoomProxy: RoomProxyProtocol {
return await Task.dispatch(on: userInitiatedDispatchQueue) { return await Task.dispatch(on: userInitiatedDispatchQueue) {
do { do {
try self.room.sendImage(url: url.path(), thumbnailUrl: thumbnailURL.path(), imageInfo: imageInfo, progressWatcher: nil) try self.room.sendImage(url: url.path(), thumbnailUrl: thumbnailURL.path(), imageInfo: imageInfo, progressWatcher: UploadProgressListener { progress in
progressSubject?.send(progress)
})
return .success(()) return .success(())
} catch { } catch {
return .failure(.failedSendingMedia) return .failure(.failedSendingMedia)
@@ -292,7 +294,7 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo) async -> Result<Void, RoomProxyError> { func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer { defer {
sendMessageBackgroundTask?.stop() sendMessageBackgroundTask?.stop()
@@ -300,7 +302,9 @@ class RoomProxy: RoomProxyProtocol {
return await Task.dispatch(on: userInitiatedDispatchQueue) { return await Task.dispatch(on: userInitiatedDispatchQueue) {
do { do {
try self.room.sendVideo(url: url.path(), thumbnailUrl: thumbnailURL.path(), videoInfo: videoInfo, progressWatcher: nil) try self.room.sendVideo(url: url.path(), thumbnailUrl: thumbnailURL.path(), videoInfo: videoInfo, progressWatcher: UploadProgressListener { progress in
progressSubject?.send(progress)
})
return .success(()) return .success(())
} catch { } catch {
return .failure(.failedSendingMedia) return .failure(.failedSendingMedia)
@@ -308,7 +312,7 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func sendAudio(url: URL, audioInfo: AudioInfo) async -> Result<Void, RoomProxyError> { func sendAudio(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer { defer {
sendMessageBackgroundTask?.stop() sendMessageBackgroundTask?.stop()
@@ -316,7 +320,9 @@ class RoomProxy: RoomProxyProtocol {
return await Task.dispatch(on: userInitiatedDispatchQueue) { return await Task.dispatch(on: userInitiatedDispatchQueue) {
do { do {
try self.room.sendAudio(url: url.path(), audioInfo: audioInfo, progressWatcher: nil) try self.room.sendAudio(url: url.path(), audioInfo: audioInfo, progressWatcher: UploadProgressListener { progress in
progressSubject?.send(progress)
})
return .success(()) return .success(())
} catch { } catch {
return .failure(.failedSendingMedia) return .failure(.failedSendingMedia)
@@ -324,7 +330,7 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func sendFile(url: URL, fileInfo: FileInfo) async -> Result<Void, RoomProxyError> { func sendFile(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) sendMessageBackgroundTask = backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer { defer {
sendMessageBackgroundTask?.stop() sendMessageBackgroundTask?.stop()
@@ -332,7 +338,9 @@ class RoomProxy: RoomProxyProtocol {
return await Task.dispatch(on: userInitiatedDispatchQueue) { return await Task.dispatch(on: userInitiatedDispatchQueue) {
do { do {
try self.room.sendFile(url: url.path(), fileInfo: fileInfo, progressWatcher: nil) try self.room.sendFile(url: url.path(), fileInfo: fileInfo, progressWatcher: UploadProgressListener { progress in
progressSubject?.send(progress)
})
return .success(()) return .success(())
} catch { } catch {
return .failure(.failedSendingMedia) return .failure(.failedSendingMedia)
@@ -611,3 +619,17 @@ private class RoomTimelineListener: TimelineListener {
onUpdateClosure(diff) onUpdateClosure(diff)
} }
} }
private class UploadProgressListener: ProgressWatcher {
private let onUpdateClosure: (Double) -> Void
init(_ onUpdateClosure: @escaping (Double) -> Void) {
self.onUpdateClosure = onUpdateClosure
}
func transmissionProgress(progress: TransmissionProgress) {
DispatchQueue.main.async { [weak self] in
self?.onUpdateClosure(Double(progress.current) / Double(progress.total))
}
}
}
@@ -93,13 +93,13 @@ protocol RoomProxyProtocol {
func sendReaction(_ reaction: String, to eventID: String) async -> Result<Void, RoomProxyError> func sendReaction(_ reaction: String, to eventID: String) async -> Result<Void, RoomProxyError>
func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo) async -> Result<Void, RoomProxyError> func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>
func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo) async -> Result<Void, RoomProxyError> func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>
func sendAudio(url: URL, audioInfo: AudioInfo) async -> Result<Void, RoomProxyError> func sendAudio(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>
func sendFile(url: URL, fileInfo: FileInfo) async -> Result<Void, RoomProxyError> func sendFile(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>
/// Retries sending a failed message given its transaction ID /// Retries sending a failed message given its transaction ID
func retrySend(transactionID: String) async func retrySend(transactionID: String) async