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

View File

@ -663,7 +663,6 @@
F508683B76EF7B23BB2CBD6D /* TimelineItemPlainStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94BCC8A9C73C1F838122C645 /* TimelineItemPlainStylerView.swift */; };
F519DE17A3A0F760307B2E6D /* InviteUsersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D155E09BF961BBA8F85263 /* InviteUsersScreenViewModel.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 */; };
F656F92A63D3DC1978D79427 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 07FEEEDB11543A7DED420F04 /* Compound */; };
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>"; };
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>"; };
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>"; };
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>"; };
@ -2983,7 +2981,6 @@
6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */,
C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */,
F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */,
F28551E81CE3700E5F1EC9B5 /* ProgressTracker.swift */,
53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */,
DBA8DC95C079805B0B56E8A9 /* SharedUserDefaultsKeys.swift */,
BB3073CCD77D906B330BC1D6 /* Tests.swift */,
@ -4153,7 +4150,6 @@
962A4F8AD6312804E2C6BB6E /* PhotoLibraryPicker.swift in Sources */,
9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */,
DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */,
F587A9AF25A262DE5A7B0369 /* ProgressTracker.swift in Sources */,
2835FD52F3F618D07F799B3D /* Publisher.swift in Sources */,
743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */,
8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */,

View File

@ -177,12 +177,12 @@ class BugReportServiceMock: BugReportServiceProtocol {
var submitBugReportProgressListenerCalled: Bool {
return submitBugReportProgressListenerCallsCount > 0
}
var submitBugReportProgressListenerReceivedArguments: (bugReport: BugReport, progressListener: ProgressListener?)?
var submitBugReportProgressListenerReceivedInvocations: [(bugReport: BugReport, progressListener: ProgressListener?)] = []
var submitBugReportProgressListenerReceivedArguments: (bugReport: BugReport, progressListener: CurrentValueSubject<Double, Never>)?
var submitBugReportProgressListenerReceivedInvocations: [(bugReport: BugReport, progressListener: CurrentValueSubject<Double, Never>)] = []
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
submitBugReportProgressListenerReceivedArguments = (bugReport: bugReport, progressListener: progressListener)
submitBugReportProgressListenerReceivedInvocations.append((bugReport: bugReport, progressListener: progressListener))
@ -633,86 +633,86 @@ class RoomProxyMock: RoomProxyProtocol {
}
//MARK: - sendImage
var sendImageUrlThumbnailURLImageInfoCallsCount = 0
var sendImageUrlThumbnailURLImageInfoCalled: Bool {
return sendImageUrlThumbnailURLImageInfoCallsCount > 0
var sendImageUrlThumbnailURLImageInfoProgressSubjectCallsCount = 0
var sendImageUrlThumbnailURLImageInfoProgressSubjectCalled: Bool {
return sendImageUrlThumbnailURLImageInfoProgressSubjectCallsCount > 0
}
var sendImageUrlThumbnailURLImageInfoReceivedArguments: (url: URL, thumbnailURL: URL, imageInfo: ImageInfo)?
var sendImageUrlThumbnailURLImageInfoReceivedInvocations: [(url: URL, thumbnailURL: URL, imageInfo: ImageInfo)] = []
var sendImageUrlThumbnailURLImageInfoReturnValue: Result<Void, RoomProxyError>!
var sendImageUrlThumbnailURLImageInfoClosure: ((URL, URL, ImageInfo) async -> Result<Void, RoomProxyError>)?
var sendImageUrlThumbnailURLImageInfoProgressSubjectReceivedArguments: (url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?)?
var sendImageUrlThumbnailURLImageInfoProgressSubjectReceivedInvocations: [(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?)] = []
var sendImageUrlThumbnailURLImageInfoProgressSubjectReturnValue: 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> {
sendImageUrlThumbnailURLImageInfoCallsCount += 1
sendImageUrlThumbnailURLImageInfoReceivedArguments = (url: url, thumbnailURL: thumbnailURL, imageInfo: imageInfo)
sendImageUrlThumbnailURLImageInfoReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, imageInfo: imageInfo))
if let sendImageUrlThumbnailURLImageInfoClosure = sendImageUrlThumbnailURLImageInfoClosure {
return await sendImageUrlThumbnailURLImageInfoClosure(url, thumbnailURL, imageInfo)
func sendImage(url: URL, thumbnailURL: URL, imageInfo: ImageInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendImageUrlThumbnailURLImageInfoProgressSubjectCallsCount += 1
sendImageUrlThumbnailURLImageInfoProgressSubjectReceivedArguments = (url: url, thumbnailURL: thumbnailURL, imageInfo: imageInfo, progressSubject: progressSubject)
sendImageUrlThumbnailURLImageInfoProgressSubjectReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, imageInfo: imageInfo, progressSubject: progressSubject))
if let sendImageUrlThumbnailURLImageInfoProgressSubjectClosure = sendImageUrlThumbnailURLImageInfoProgressSubjectClosure {
return await sendImageUrlThumbnailURLImageInfoProgressSubjectClosure(url, thumbnailURL, imageInfo, progressSubject)
} else {
return sendImageUrlThumbnailURLImageInfoReturnValue
return sendImageUrlThumbnailURLImageInfoProgressSubjectReturnValue
}
}
//MARK: - sendVideo
var sendVideoUrlThumbnailURLVideoInfoCallsCount = 0
var sendVideoUrlThumbnailURLVideoInfoCalled: Bool {
return sendVideoUrlThumbnailURLVideoInfoCallsCount > 0
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectCallsCount = 0
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectCalled: Bool {
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectCallsCount > 0
}
var sendVideoUrlThumbnailURLVideoInfoReceivedArguments: (url: URL, thumbnailURL: URL, videoInfo: VideoInfo)?
var sendVideoUrlThumbnailURLVideoInfoReceivedInvocations: [(url: URL, thumbnailURL: URL, videoInfo: VideoInfo)] = []
var sendVideoUrlThumbnailURLVideoInfoReturnValue: Result<Void, RoomProxyError>!
var sendVideoUrlThumbnailURLVideoInfoClosure: ((URL, URL, VideoInfo) async -> Result<Void, RoomProxyError>)?
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectReceivedArguments: (url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?)?
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectReceivedInvocations: [(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?)] = []
var sendVideoUrlThumbnailURLVideoInfoProgressSubjectReturnValue: 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> {
sendVideoUrlThumbnailURLVideoInfoCallsCount += 1
sendVideoUrlThumbnailURLVideoInfoReceivedArguments = (url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo)
sendVideoUrlThumbnailURLVideoInfoReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo))
if let sendVideoUrlThumbnailURLVideoInfoClosure = sendVideoUrlThumbnailURLVideoInfoClosure {
return await sendVideoUrlThumbnailURLVideoInfoClosure(url, thumbnailURL, videoInfo)
func sendVideo(url: URL, thumbnailURL: URL, videoInfo: VideoInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendVideoUrlThumbnailURLVideoInfoProgressSubjectCallsCount += 1
sendVideoUrlThumbnailURLVideoInfoProgressSubjectReceivedArguments = (url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo, progressSubject: progressSubject)
sendVideoUrlThumbnailURLVideoInfoProgressSubjectReceivedInvocations.append((url: url, thumbnailURL: thumbnailURL, videoInfo: videoInfo, progressSubject: progressSubject))
if let sendVideoUrlThumbnailURLVideoInfoProgressSubjectClosure = sendVideoUrlThumbnailURLVideoInfoProgressSubjectClosure {
return await sendVideoUrlThumbnailURLVideoInfoProgressSubjectClosure(url, thumbnailURL, videoInfo, progressSubject)
} else {
return sendVideoUrlThumbnailURLVideoInfoReturnValue
return sendVideoUrlThumbnailURLVideoInfoProgressSubjectReturnValue
}
}
//MARK: - sendAudio
var sendAudioUrlAudioInfoCallsCount = 0
var sendAudioUrlAudioInfoCalled: Bool {
return sendAudioUrlAudioInfoCallsCount > 0
var sendAudioUrlAudioInfoProgressSubjectCallsCount = 0
var sendAudioUrlAudioInfoProgressSubjectCalled: Bool {
return sendAudioUrlAudioInfoProgressSubjectCallsCount > 0
}
var sendAudioUrlAudioInfoReceivedArguments: (url: URL, audioInfo: AudioInfo)?
var sendAudioUrlAudioInfoReceivedInvocations: [(url: URL, audioInfo: AudioInfo)] = []
var sendAudioUrlAudioInfoReturnValue: Result<Void, RoomProxyError>!
var sendAudioUrlAudioInfoClosure: ((URL, AudioInfo) async -> Result<Void, RoomProxyError>)?
var sendAudioUrlAudioInfoProgressSubjectReceivedArguments: (url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?)?
var sendAudioUrlAudioInfoProgressSubjectReceivedInvocations: [(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?)] = []
var sendAudioUrlAudioInfoProgressSubjectReturnValue: Result<Void, RoomProxyError>!
var sendAudioUrlAudioInfoProgressSubjectClosure: ((URL, AudioInfo, CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>)?
func sendAudio(url: URL, audioInfo: AudioInfo) async -> Result<Void, RoomProxyError> {
sendAudioUrlAudioInfoCallsCount += 1
sendAudioUrlAudioInfoReceivedArguments = (url: url, audioInfo: audioInfo)
sendAudioUrlAudioInfoReceivedInvocations.append((url: url, audioInfo: audioInfo))
if let sendAudioUrlAudioInfoClosure = sendAudioUrlAudioInfoClosure {
return await sendAudioUrlAudioInfoClosure(url, audioInfo)
func sendAudio(url: URL, audioInfo: AudioInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendAudioUrlAudioInfoProgressSubjectCallsCount += 1
sendAudioUrlAudioInfoProgressSubjectReceivedArguments = (url: url, audioInfo: audioInfo, progressSubject: progressSubject)
sendAudioUrlAudioInfoProgressSubjectReceivedInvocations.append((url: url, audioInfo: audioInfo, progressSubject: progressSubject))
if let sendAudioUrlAudioInfoProgressSubjectClosure = sendAudioUrlAudioInfoProgressSubjectClosure {
return await sendAudioUrlAudioInfoProgressSubjectClosure(url, audioInfo, progressSubject)
} else {
return sendAudioUrlAudioInfoReturnValue
return sendAudioUrlAudioInfoProgressSubjectReturnValue
}
}
//MARK: - sendFile
var sendFileUrlFileInfoCallsCount = 0
var sendFileUrlFileInfoCalled: Bool {
return sendFileUrlFileInfoCallsCount > 0
var sendFileUrlFileInfoProgressSubjectCallsCount = 0
var sendFileUrlFileInfoProgressSubjectCalled: Bool {
return sendFileUrlFileInfoProgressSubjectCallsCount > 0
}
var sendFileUrlFileInfoReceivedArguments: (url: URL, fileInfo: FileInfo)?
var sendFileUrlFileInfoReceivedInvocations: [(url: URL, fileInfo: FileInfo)] = []
var sendFileUrlFileInfoReturnValue: Result<Void, RoomProxyError>!
var sendFileUrlFileInfoClosure: ((URL, FileInfo) async -> Result<Void, RoomProxyError>)?
var sendFileUrlFileInfoProgressSubjectReceivedArguments: (url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?)?
var sendFileUrlFileInfoProgressSubjectReceivedInvocations: [(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?)] = []
var sendFileUrlFileInfoProgressSubjectReturnValue: Result<Void, RoomProxyError>!
var sendFileUrlFileInfoProgressSubjectClosure: ((URL, FileInfo, CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError>)?
func sendFile(url: URL, fileInfo: FileInfo) async -> Result<Void, RoomProxyError> {
sendFileUrlFileInfoCallsCount += 1
sendFileUrlFileInfoReceivedArguments = (url: url, fileInfo: fileInfo)
sendFileUrlFileInfoReceivedInvocations.append((url: url, fileInfo: fileInfo))
if let sendFileUrlFileInfoClosure = sendFileUrlFileInfoClosure {
return await sendFileUrlFileInfoClosure(url, fileInfo)
func sendFile(url: URL, fileInfo: FileInfo, progressSubject: CurrentValueSubject<Double, Never>?) async -> Result<Void, RoomProxyError> {
sendFileUrlFileInfoProgressSubjectCallsCount += 1
sendFileUrlFileInfoProgressSubjectReceivedArguments = (url: url, fileInfo: fileInfo, progressSubject: progressSubject)
sendFileUrlFileInfoProgressSubjectReceivedInvocations.append((url: url, fileInfo: fileInfo, progressSubject: progressSubject))
if let sendFileUrlFileInfoProgressSubjectClosure = sendFileUrlFileInfoProgressSubjectClosure {
return await sendFileUrlFileInfoProgressSubjectClosure(url, fileInfo, progressSubject)
} else {
return sendFileUrlFileInfoReturnValue
return sendFileUrlFileInfoProgressSubjectReturnValue
}
}
//MARK: - retrySend

View File

@ -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)
}
}

View File

@ -30,13 +30,13 @@ struct UserIndicator: Equatable, Identifiable {
static func == (lhs: UserIndicator.Progress, rhs: UserIndicator.Progress) -> Bool {
switch (lhs, rhs) {
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
}
}
case indeterminate
case published(ProgressPublisher)
case published(CurrentValuePublisher<Double, Never>)
}
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 {
case .toast(let progress), .modal(let progress, _):
switch progress {
case .none, .indeterminate:
return Empty().eraseToAnyPublisher()
case .some(.published(let progress)):
return progress.publisher.eraseToAnyPublisher()
return CurrentValueSubject<Double, Never>(0.0).asCurrentValuePublisher()
case .some(.published(let publisher)):
return publisher
}
}
}

View File

@ -69,7 +69,7 @@ struct UserIndicatorModalView_Previews: PreviewProvider {
)
.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),
title: "Successfully logged in",
iconName: "checkmark")

View File

@ -60,8 +60,8 @@ final class BugReportScreenCoordinator: CoordinatorProtocol {
switch result {
case .cancel:
self.completion?(.cancel)
case let .submitStarted(progressTracker):
self.startLoading(label: L10n.commonSending, progressPublisher: progressTracker)
case let .submitStarted(progressPublisher):
self.startLoading(label: L10n.commonSending, progressPublisher: progressPublisher)
case .submitFinished:
self.stopLoading()
self.completion?(.finish)
@ -85,7 +85,7 @@ final class BugReportScreenCoordinator: CoordinatorProtocol {
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(
UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal(progress: .published(progressPublisher), interactiveDismissDisabled: false),

View File

@ -19,7 +19,7 @@ import UIKit
enum BugReportScreenViewModelAction {
case cancel
case submitStarted(progressTracker: ProgressTracker)
case submitStarted(progressPublisher: CurrentValuePublisher<Double, Never>)
case submitFinished
case submitFailed(error: Error)
}

View File

@ -62,8 +62,9 @@ class BugReportScreenViewModel: BugReportScreenViewModelType, BugReportScreenVie
// MARK: Private
private func submitBugReport() async {
let progressTracker = ProgressTracker()
actionsSubject.send(.submitStarted(progressTracker: progressTracker))
let progressSubject = CurrentValueSubject<Double, Never>(0.0)
actionsSubject.send(.submitStarted(progressPublisher: progressSubject.asCurrentValuePublisher()))
var files: [URL] = []
if let screenshot = context.viewState.screenshot,
@ -87,7 +88,7 @@ class BugReportScreenViewModel: BugReportScreenViewModelType, BugReportScreenVie
files: files)
switch await bugReportService.submitBugReport(bugReport,
progressListener: progressTracker) {
progressListener: progressSubject) {
case .success(let response):
MXLog.info("Submission uploaded to: \(response.reportUrl)")
actionsSubject.send(.submitFinished)

View File

@ -14,6 +14,7 @@
// limitations under the License.
//
import Combine
import SwiftUI
typealias MediaUploadPreviewScreenViewModelType = StateStoreViewModel<MediaUploadPreviewScreenViewState, MediaUploadPreviewScreenViewAction>
@ -43,11 +44,13 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
switch viewAction {
case .send:
Task {
startLoading()
let progressSubject = CurrentValueSubject<Double, Never>(0.0)
startLoading(progressPublisher: progressSubject.asCurrentValuePublisher())
switch await mediaUploadingPreprocessor.processMedia(at: url) {
case .success(let mediaInfo):
switch await sendAttachment(mediaInfo: mediaInfo) {
switch await sendAttachment(mediaInfo: mediaInfo, progressSubject: progressSubject) {
case .success:
callback?(.dismiss)
case .failure(let error):
@ -70,26 +73,26 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
// 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 {
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):
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):
return await roomProxy.sendAudio(url: audioURL, audioInfo: audioInfo)
return await roomProxy.sendAudio(url: audioURL, audioInfo: audioInfo, progressSubject: progressSubject)
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 func startLoading() {
private func startLoading(progressPublisher: CurrentValuePublisher<Double, Never>) {
userIndicatorController?.submitIndicator(
UserIndicator(id: Self.loadingIndicatorIdentifier,
type: .modal,
title: L10n.commonLoading,
type: .modal(progress: .published(progressPublisher), interactiveDismissDisabled: false),
title: L10n.commonSending,
persistent: true)
)
}

View File

@ -100,7 +100,7 @@ class BugReportService: NSObject, BugReportServiceProtocol {
// swiftlint:disable:next function_body_length cyclomatic_complexity
func submitBugReport(_ bugReport: BugReport,
progressListener: ProgressListener?) async -> Result<SubmitBugReportResponse, BugReportServiceError> {
progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError> {
var params = [
MultipartFormData(key: "user_id", type: .text(value: bugReport.userID)),
MultipartFormData(key: "text", type: .text(value: bugReport.text))
@ -148,17 +148,13 @@ class BugReportService: NSObject, BugReportServiceProtocol {
request.httpMethod = "POST"
request.httpBody = body as Data
var delegate: URLSessionTaskDelegate?
if let progressListener {
progressSubject
.receive(on: DispatchQueue.main)
.weakAssign(to: \.value, on: progressListener.progressSubject)
.store(in: &cancellables)
delegate = self
}
progressSubject
.receive(on: DispatchQueue.main)
.weakAssign(to: \.value, on: progressListener)
.store(in: &cancellables)
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 {
let errorDescription = String(data: data, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "Unknown"

View File

@ -14,6 +14,7 @@
// limitations under the License.
//
import Combine
import Foundation
import UIKit
@ -63,5 +64,5 @@ protocol BugReportServiceProtocol {
func crash()
func submitBugReport(_ bugReport: BugReport,
progressListener: ProgressListener?) async -> Result<SubmitBugReportResponse, BugReportServiceError>
progressListener: CurrentValueSubject<Double, Never>) async -> Result<SubmitBugReportResponse, BugReportServiceError>
}

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)
defer {
sendMessageBackgroundTask?.stop()
@ -284,7 +284,9 @@ class RoomProxy: RoomProxyProtocol {
return await Task.dispatch(on: userInitiatedDispatchQueue) {
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(())
} catch {
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)
defer {
sendMessageBackgroundTask?.stop()
@ -300,7 +302,9 @@ class RoomProxy: RoomProxyProtocol {
return await Task.dispatch(on: userInitiatedDispatchQueue) {
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(())
} catch {
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)
defer {
sendMessageBackgroundTask?.stop()
@ -316,7 +320,9 @@ class RoomProxy: RoomProxyProtocol {
return await Task.dispatch(on: userInitiatedDispatchQueue) {
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(())
} catch {
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)
defer {
sendMessageBackgroundTask?.stop()
@ -332,7 +338,9 @@ class RoomProxy: RoomProxyProtocol {
return await Task.dispatch(on: userInitiatedDispatchQueue) {
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(())
} catch {
return .failure(.failedSendingMedia)
@ -611,3 +619,17 @@ private class RoomTimelineListener: TimelineListener {
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))
}
}
}

View File

@ -93,13 +93,13 @@ protocol RoomProxyProtocol {
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
func retrySend(transactionID: String) async