metatext-app-ios-iphone-ipad/ViewModels/Sources/ViewModels/CompositionViewModel.swift

123 lines
4.4 KiB
Swift
Raw Normal View History

2020-12-10 03:44:06 +01:00
// Copyright © 2020 Metabolist. All rights reserved.
import Combine
import Foundation
import Mastodon
import ServiceLayer
2021-01-08 07:11:33 +01:00
public final class CompositionViewModel: AttachmentsRenderingViewModel, ObservableObject, Identifiable {
2020-12-18 01:17:17 +01:00
public let id = Id()
2020-12-19 07:30:19 +01:00
public var isPosted = false
2020-12-18 01:17:17 +01:00
@Published public var text = ""
2021-01-01 01:49:59 +01:00
@Published public var contentWarning = ""
@Published public var displayContentWarning = false
2021-01-08 07:11:33 +01:00
@Published public private(set) var attachmentViewModels = [AttachmentViewModel]()
2020-12-17 07:48:06 +01:00
@Published public private(set) var attachmentUpload: AttachmentUpload?
2021-01-03 23:37:06 +01:00
@Published public private(set) var isPostable = false
@Published public private(set) var canAddAttachment = true
@Published public private(set) var canAddNonImageAttachment = true
2021-01-04 02:15:50 +01:00
@Published public private(set) var remainingCharacters = CompositionViewModel.maxCharacters
2021-01-08 07:11:33 +01:00
public let canRemoveAttachments = true
2020-12-10 03:44:06 +01:00
2021-01-04 00:57:40 +01:00
private var attachmentUploadCancellable: AnyCancellable?
2020-12-16 02:39:38 +01:00
2021-01-01 01:49:59 +01:00
init() {
$text.map { !$0.isEmpty }
.removeDuplicates()
.combineLatest($attachmentViewModels.map { !$0.isEmpty })
.map { textPresent, attachmentPresent in
textPresent || attachmentPresent
}
.assign(to: &$isPostable)
2021-01-03 23:37:06 +01:00
$attachmentViewModels
.combineLatest($attachmentUpload)
.map { $0.count < Self.maxAttachmentCount && $1 == nil }
.assign(to: &$canAddAttachment)
$attachmentViewModels.map(\.isEmpty).assign(to: &$canAddNonImageAttachment)
2021-01-04 02:15:50 +01:00
$text.map {
let tokens = $0.components(separatedBy: " ")
return tokens.map(\.countShorteningIfURL).reduce(tokens.count - 1, +)
}
.combineLatest($displayContentWarning, $contentWarning)
.map { Self.maxCharacters - ($0 + ($1 ? $2.count : 0)) }
.assign(to: &$remainingCharacters)
2020-12-16 02:39:38 +01:00
}
2021-01-08 07:11:33 +01:00
public func attachmentSelected(viewModel: AttachmentViewModel) {
}
public func removeAttachment(viewModel: AttachmentViewModel) {
attachmentViewModels.removeAll { $0 === viewModel }
}
2020-12-16 02:39:38 +01:00
}
public extension CompositionViewModel {
2021-01-04 02:15:50 +01:00
static let maxCharacters = 500
2020-12-18 01:17:17 +01:00
typealias Id = UUID
2020-12-16 02:39:38 +01:00
enum Event {
case insertAfter(CompositionViewModel)
case presentMediaPicker(CompositionViewModel)
2020-12-17 07:48:06 +01:00
case error(Error)
}
2021-01-01 01:49:59 +01:00
func components(inReplyToId: Status.Id?, visibility: Status.Visibility) -> StatusComponents {
2020-12-19 07:30:19 +01:00
StatusComponents(
inReplyToId: inReplyToId,
text: text,
2021-01-01 01:49:59 +01:00
spoilerText: displayContentWarning ? contentWarning : "",
mediaIds: attachmentViewModels.map(\.attachment.id),
visibility: visibility)
2020-12-16 02:39:38 +01:00
}
2021-01-04 00:57:40 +01:00
func cancelUpload() {
attachmentUploadCancellable?.cancel()
}
2021-01-01 01:49:59 +01:00
}
2020-12-16 02:39:38 +01:00
2021-01-01 01:49:59 +01:00
extension CompositionViewModel {
2021-01-04 00:57:40 +01:00
func attach(itemProvider: NSItemProvider, parentViewModel: NewStatusViewModel) {
attachmentUploadCancellable = MediaProcessingService.dataAndMimeType(itemProvider: itemProvider)
2020-12-17 07:48:06 +01:00
.flatMap { [weak self] data, mimeType -> AnyPublisher<Attachment, Error> in
guard let self = self else { return Empty().eraseToAnyPublisher() }
2021-01-01 01:49:59 +01:00
let progress = Progress(totalUnitCount: 1)
2020-12-17 07:48:06 +01:00
DispatchQueue.main.async {
self.attachmentUpload = AttachmentUpload(progress: progress, data: data, mimeType: mimeType)
}
2021-01-04 00:57:40 +01:00
return parentViewModel.identification.service.uploadAttachment(
data: data,
mimeType: mimeType,
progress: progress)
2020-12-17 07:48:06 +01:00
}
2020-12-19 07:30:19 +01:00
.receive(on: DispatchQueue.main)
2021-01-04 00:57:40 +01:00
.assignErrorsToAlertItem(to: \.alertItem, on: parentViewModel)
.handleEvents(receiveCancel: { [weak self] in self?.attachmentUpload = nil })
.sink { [weak self] _ in
self?.attachmentUpload = nil
} receiveValue: { [weak self] in
2021-01-08 07:11:33 +01:00
self?.attachmentViewModels.append(
AttachmentViewModel(
attachment: $0,
identification: parentViewModel.identification))
2021-01-04 00:57:40 +01:00
}
2020-12-19 07:30:19 +01:00
}
2020-12-10 03:44:06 +01:00
}
2021-01-03 23:37:06 +01:00
private extension CompositionViewModel {
static let maxAttachmentCount = 4
}
2021-01-04 02:15:50 +01:00
private extension String {
static let urlCharacterCount = 23
2021-01-04 02:52:37 +01:00
var countShorteningIfURL: Int {
starts(with: "http://") || starts(with: "https://") ? Self.urlCharacterCount : count
}
2021-01-04 02:15:50 +01:00
}