2021-06-29 10:41:58 +02:00
|
|
|
//
|
|
|
|
// ComposeStatusAttachmentTableViewCell.swift
|
|
|
|
// Mastodon
|
|
|
|
//
|
|
|
|
// Created by MainasuK Cirno on 2021-6-29.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
import Combine
|
|
|
|
import AlamofireImage
|
2022-01-27 14:23:39 +01:00
|
|
|
import MastodonAsset
|
|
|
|
import MastodonLocalization
|
2021-06-29 10:41:58 +02:00
|
|
|
|
|
|
|
final class ComposeStatusAttachmentTableViewCell: UITableViewCell {
|
|
|
|
|
|
|
|
private(set) var dataSource: UICollectionViewDiffableDataSource<ComposeStatusAttachmentSection, ComposeStatusAttachmentItem>!
|
|
|
|
weak var composeStatusAttachmentCollectionViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate?
|
|
|
|
var observations = Set<NSKeyValueObservation>()
|
|
|
|
|
|
|
|
private static func createLayout() -> UICollectionViewLayout {
|
|
|
|
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
|
|
|
|
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
|
|
|
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))
|
|
|
|
let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
|
|
|
|
let section = NSCollectionLayoutSection(group: group)
|
|
|
|
section.contentInsetsReference = .readableContent
|
|
|
|
return UICollectionViewCompositionalLayout(section: section)
|
|
|
|
}
|
|
|
|
|
|
|
|
private(set) var collectionViewHeightLayoutConstraint: NSLayoutConstraint!
|
2021-06-29 11:08:41 +02:00
|
|
|
let collectionView: UICollectionView = {
|
2021-06-29 10:41:58 +02:00
|
|
|
let collectionViewLayout = ComposeStatusAttachmentTableViewCell.createLayout()
|
2021-06-29 11:08:41 +02:00
|
|
|
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
2021-06-29 10:41:58 +02:00
|
|
|
collectionView.register(ComposeStatusAttachmentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self))
|
2021-07-05 10:07:17 +02:00
|
|
|
collectionView.backgroundColor = .clear
|
2021-06-29 10:41:58 +02:00
|
|
|
collectionView.alwaysBounceVertical = true
|
|
|
|
collectionView.isScrollEnabled = false
|
|
|
|
return collectionView
|
|
|
|
}()
|
2021-09-29 10:27:35 +02:00
|
|
|
let collectionViewHeightDidUpdate = PassthroughSubject<Void, Never>()
|
2021-06-29 10:41:58 +02:00
|
|
|
|
|
|
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
|
|
_init()
|
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
super.init(coder: coder)
|
|
|
|
_init()
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extension ComposeStatusAttachmentTableViewCell {
|
|
|
|
|
|
|
|
private func _init() {
|
2021-07-05 10:07:17 +02:00
|
|
|
backgroundColor = .clear
|
|
|
|
contentView.backgroundColor = .clear
|
|
|
|
|
2021-06-29 10:41:58 +02:00
|
|
|
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
contentView.addSubview(collectionView)
|
|
|
|
collectionViewHeightLayoutConstraint = collectionView.heightAnchor.constraint(equalToConstant: 200).priority(.defaultHigh)
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
collectionView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
|
|
|
collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
|
|
|
collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
|
|
|
collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
|
|
|
collectionViewHeightLayoutConstraint,
|
|
|
|
])
|
|
|
|
|
|
|
|
collectionView.observe(\.contentSize, options: [.initial, .new]) { [weak self] collectionView, _ in
|
|
|
|
guard let self = self else { return }
|
|
|
|
self.collectionViewHeightLayoutConstraint.constant = collectionView.contentSize.height
|
2021-09-29 10:27:35 +02:00
|
|
|
self.collectionViewHeightDidUpdate.send()
|
2021-06-29 10:41:58 +02:00
|
|
|
}
|
|
|
|
.store(in: &observations)
|
|
|
|
|
|
|
|
self.dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { [
|
|
|
|
weak self
|
|
|
|
] collectionView, indexPath, item -> UICollectionViewCell? in
|
|
|
|
guard let self = self else { return UICollectionViewCell() }
|
|
|
|
switch item {
|
|
|
|
case .attachment(let attachmentService):
|
|
|
|
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self), for: indexPath) as! ComposeStatusAttachmentCollectionViewCell
|
|
|
|
cell.attachmentContainerView.descriptionTextView.text = attachmentService.description.value
|
|
|
|
cell.delegate = self.composeStatusAttachmentCollectionViewCellDelegate
|
|
|
|
attachmentService.thumbnailImage
|
|
|
|
.receive(on: DispatchQueue.main)
|
|
|
|
.sink { [weak cell] thumbnailImage in
|
|
|
|
guard let cell = cell else { return }
|
|
|
|
let size = cell.attachmentContainerView.previewImageView.frame.size != .zero ? cell.attachmentContainerView.previewImageView.frame.size : CGSize(width: 1, height: 1)
|
|
|
|
guard let image = thumbnailImage else {
|
|
|
|
let placeholder = UIImage.placeholder(
|
|
|
|
size: size,
|
2021-07-20 13:24:24 +02:00
|
|
|
color: ThemeService.shared.currentTheme.value.systemGroupedBackgroundColor
|
2021-06-29 10:41:58 +02:00
|
|
|
)
|
|
|
|
.af.imageRounded(
|
|
|
|
withCornerRadius: AttachmentContainerView.containerViewCornerRadius
|
|
|
|
)
|
|
|
|
cell.attachmentContainerView.previewImageView.image = placeholder
|
|
|
|
return
|
|
|
|
}
|
2021-06-30 10:14:33 +02:00
|
|
|
// cannot get correct size. set corner radius on layer
|
2021-06-29 10:41:58 +02:00
|
|
|
cell.attachmentContainerView.previewImageView.image = image
|
|
|
|
}
|
|
|
|
.store(in: &cell.disposeBag)
|
|
|
|
Publishers.CombineLatest(
|
|
|
|
attachmentService.uploadStateMachineSubject.eraseToAnyPublisher(),
|
|
|
|
attachmentService.error.eraseToAnyPublisher()
|
|
|
|
)
|
|
|
|
.receive(on: DispatchQueue.main)
|
|
|
|
.sink { [weak cell, weak attachmentService] uploadState, error in
|
|
|
|
guard let cell = cell else { return }
|
|
|
|
guard let attachmentService = attachmentService else { return }
|
|
|
|
cell.attachmentContainerView.emptyStateView.isHidden = error == nil
|
|
|
|
cell.attachmentContainerView.descriptionBackgroundView.isHidden = error != nil
|
|
|
|
if let error = error {
|
|
|
|
cell.attachmentContainerView.activityIndicatorView.stopAnimating()
|
|
|
|
cell.attachmentContainerView.emptyStateView.label.text = error.localizedDescription
|
|
|
|
} else {
|
|
|
|
guard let uploadState = uploadState else { return }
|
|
|
|
switch uploadState {
|
2021-07-20 10:40:04 +02:00
|
|
|
case is MastodonAttachmentService.UploadState.Finish:
|
2021-06-29 10:41:58 +02:00
|
|
|
cell.attachmentContainerView.activityIndicatorView.stopAnimating()
|
2021-07-20 10:40:04 +02:00
|
|
|
case is MastodonAttachmentService.UploadState.Fail:
|
|
|
|
cell.attachmentContainerView.activityIndicatorView.stopAnimating()
|
|
|
|
// FIXME: not display
|
2021-06-29 10:41:58 +02:00
|
|
|
cell.attachmentContainerView.emptyStateView.label.text = {
|
|
|
|
if let file = attachmentService.file.value {
|
|
|
|
switch file {
|
|
|
|
case .jpeg, .png, .gif:
|
|
|
|
return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.photo)
|
|
|
|
case .other:
|
|
|
|
return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.video)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return L10n.Scene.Compose.Attachment.attachmentBroken(L10n.Scene.Compose.Attachment.photo)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.store(in: &cell.disposeBag)
|
|
|
|
NotificationCenter.default.publisher(
|
|
|
|
for: UITextView.textDidChangeNotification,
|
|
|
|
object: cell.attachmentContainerView.descriptionTextView
|
|
|
|
)
|
|
|
|
.receive(on: DispatchQueue.main)
|
|
|
|
.sink { notification in
|
|
|
|
guard let textField = notification.object as? UITextView else { return }
|
|
|
|
let text = textField.text?.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
|
attachmentService.description.value = text
|
|
|
|
}
|
|
|
|
.store(in: &cell.disposeBag)
|
|
|
|
return cell
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|