feat: make image attachments uploading in the queue

This commit is contained in:
CMK 2021-03-22 18:40:32 +08:00
parent d64a06aa9d
commit c35fcfb08f
5 changed files with 60 additions and 34 deletions

View File

@ -147,8 +147,9 @@ extension ComposeViewController {
]) ])
collectionView.delegate = self collectionView.delegate = self
let longPressReorderGesture = UILongPressGestureRecognizer(target: self, action: #selector(ComposeViewController.longPressReorderGestureHandler(_:))) // Note: do not allow reorder due to the images display order following the upload time
collectionView.addGestureRecognizer(longPressReorderGesture) // let longPressReorderGesture = UILongPressGestureRecognizer(target: self, action: #selector(ComposeViewController.longPressReorderGestureHandler(_:)))
// collectionView.addGestureRecognizer(longPressReorderGesture)
viewModel.setupDiffableDataSource( viewModel.setupDiffableDataSource(
for: collectionView, for: collectionView,
dependency: self, dependency: self,
@ -321,7 +322,7 @@ extension ComposeViewController {
dismiss(animated: true, completion: nil) dismiss(animated: true, completion: nil)
} }
/* Do not allow reorder image due to image display order following the update time
@objc private func longPressReorderGestureHandler(_ sender: UILongPressGestureRecognizer) { @objc private func longPressReorderGestureHandler(_ sender: UILongPressGestureRecognizer) {
switch(sender.state) { switch(sender.state) {
case .began: case .began:
@ -347,6 +348,7 @@ extension ComposeViewController {
collectionView.cancelInteractiveMovement() collectionView.cancelInteractiveMovement()
} }
} }
*/
} }

View File

@ -24,27 +24,29 @@ extension ComposeViewModel {
textEditorViewTextAttributesDelegate: textEditorViewTextAttributesDelegate, textEditorViewTextAttributesDelegate: textEditorViewTextAttributesDelegate,
composeStatusAttachmentTableViewCellDelegate: composeStatusAttachmentTableViewCellDelegate composeStatusAttachmentTableViewCellDelegate: composeStatusAttachmentTableViewCellDelegate
) )
// Note: do not allow reorder due to the images display order following the upload time
// diffableDataSource.reorderingHandlers.canReorderItem = { item in
// switch item {
// case .attachment: return true
// default: return false
// }
//
// }
// diffableDataSource.reorderingHandlers.didReorder = { [weak self] transaction in
// guard let self = self else { return }
//
// let items = transaction.finalSnapshot.itemIdentifiers
// var attachmentServices: [MastodonAttachmentService] = []
// for item in items {
// guard case let .attachment(attachmentService) = item else { continue }
// attachmentServices.append(attachmentService)
// }
// self.attachmentServices.value = attachmentServices
// }
//
diffableDataSource.reorderingHandlers.canReorderItem = { item in
switch item {
case .attachment: return true
default: return false
}
}
diffableDataSource.reorderingHandlers.didReorder = { [weak self] transaction in
guard let self = self else { return }
let items = transaction.finalSnapshot.itemIdentifiers
var attachmentServices: [MastodonAttachmentService] = []
for item in items {
guard case let .attachment(attachmentService) = item else { continue }
attachmentServices.append(attachmentService)
}
self.attachmentServices.value = attachmentServices
}
self.diffableDataSource = diffableDataSource self.diffableDataSource = diffableDataSource
var snapshot = NSDiffableDataSourceSnapshot<ComposeStatusSection, ComposeStatusItem>() var snapshot = NSDiffableDataSourceSnapshot<ComposeStatusSection, ComposeStatusItem>()
snapshot.appendSections([.repliedTo, .status, .attachment]) snapshot.appendSections([.repliedTo, .status, .attachment])
switch composeKind { switch composeKind {

View File

@ -137,7 +137,7 @@ final class ComposeViewModel {
} }
.store(in: &disposeBag) .store(in: &disposeBag)
// bind snapshot // bind snapshot and drive service upload state
attachmentServices attachmentServices
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] attachmentServices in .sink { [weak self] attachmentServices in
@ -154,6 +154,26 @@ final class ComposeViewModel {
snapshot.appendItems(items, toSection: .attachment) snapshot.appendItems(items, toSection: .attachment)
diffableDataSource.apply(snapshot) diffableDataSource.apply(snapshot)
// make image upload in the queue
for attachmentService in attachmentServices {
// skip when prefix N task when task finish OR fail OR uploading
guard let currentState = attachmentService.uploadStateMachine.currentState else { break }
if currentState is MastodonAttachmentService.UploadState.Fail {
continue
}
if currentState is MastodonAttachmentService.UploadState.Finish {
continue
}
if currentState is MastodonAttachmentService.UploadState.Uploading {
break
}
// trigger uploading one by one
if currentState is MastodonAttachmentService.UploadState.Initial {
attachmentService.uploadStateMachine.enter(MastodonAttachmentService.UploadState.Uploading.self)
break
}
}
} }
.store(in: &disposeBag) .store(in: &disposeBag)
} }

View File

@ -31,8 +31,15 @@ extension MastodonAttachmentService.UploadState {
class Initial: MastodonAttachmentService.UploadState { class Initial: MastodonAttachmentService.UploadState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool { override func isValidNextState(_ stateClass: AnyClass) -> Bool {
guard service?.authenticationBox != nil else { return false } guard service?.authenticationBox != nil else { return false }
guard service?.imageData.value != nil else { return false } if stateClass == Initial.self {
return stateClass == Uploading.self return true
}
if service?.imageData.value != nil {
return stateClass == Uploading.self
} else {
return stateClass == Fail.self
}
} }
} }

View File

@ -64,15 +64,14 @@ final class MastodonAttachmentService {
switch completion { switch completion {
case .failure(let error): case .failure(let error):
self.error.value = error self.error.value = error
self.uploadStateMachine.enter(UploadState.Fail.self)
case .finished: case .finished:
break break
} }
} receiveValue: { [weak self] imageData in } receiveValue: { [weak self] imageData in
guard let self = self else { return } guard let self = self else { return }
self.imageData.value = imageData self.imageData.value = imageData
self.uploadStateMachine.enter(UploadState.Initial.self)
// Try pre-upload attachment for current active user
self.uploadStateMachine.enter(UploadState.Uploading.self)
} }
.store(in: &disposeBag) .store(in: &disposeBag)
} }
@ -89,9 +88,7 @@ final class MastodonAttachmentService {
setupServiceObserver() setupServiceObserver()
imageData.value = image.jpegData(compressionQuality: 0.75) imageData.value = image.jpegData(compressionQuality: 0.75)
uploadStateMachine.enter(UploadState.Initial.self)
// Try pre-upload attachment for current active user
uploadStateMachine.enter(UploadState.Uploading.self)
} }
init( init(
@ -106,9 +103,7 @@ final class MastodonAttachmentService {
setupServiceObserver() setupServiceObserver()
self.imageData.value = imageData self.imageData.value = imageData
uploadStateMachine.enter(UploadState.Initial.self)
// Try pre-upload attachment for current active user
uploadStateMachine.enter(UploadState.Uploading.self)
} }
private func setupServiceObserver() { private func setupServiceObserver() {