feat: add image attachments reorder support for status compose scene
This commit is contained in:
parent
36b42ba3e7
commit
b296b21ef0
|
@ -179,8 +179,8 @@
|
|||
DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; };
|
||||
DB789A0B25F9F2950071ACA0 /* ComposeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */; };
|
||||
DB789A1225F9F2CC0071ACA0 /* ComposeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */; };
|
||||
DB789A1C25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift */; };
|
||||
DB789A2B25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.swift */; };
|
||||
DB789A1C25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift */; };
|
||||
DB789A2B25F9F7AB0071ACA0 /* ComposeRepliedToTootContentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentCollectionViewCell.swift */; };
|
||||
DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */; };
|
||||
DB89B9F725C10FD0008580ED /* CoreDataStack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */; };
|
||||
DB89B9FE25C10FD0008580ED /* CoreDataStackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB89B9FD25C10FD0008580ED /* CoreDataStackTests.swift */; };
|
||||
|
@ -468,8 +468,8 @@
|
|||
DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = "<group>"; };
|
||||
DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewController.swift; sourceTree = "<group>"; };
|
||||
DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewModel.swift; sourceTree = "<group>"; };
|
||||
DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusContentTableViewCell.swift; sourceTree = "<group>"; };
|
||||
DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeRepliedToTootContentTableViewCell.swift; sourceTree = "<group>"; };
|
||||
DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusContentCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeRepliedToTootContentCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentContainerView.swift; sourceTree = "<group>"; };
|
||||
DB89B9EE25C10FD0008580ED /* CoreDataStack.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreDataStack.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DB89B9F025C10FD0008580ED /* CoreDataStack.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CoreDataStack.h; sourceTree = "<group>"; };
|
||||
|
@ -1122,7 +1122,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
DB55D32225FB4D320002F825 /* View */,
|
||||
DB789A2125F9F76D0071ACA0 /* TableViewCell */,
|
||||
DB789A2125F9F76D0071ACA0 /* CollectionViewCell */,
|
||||
DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */,
|
||||
DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */,
|
||||
DB9A488926034D40008B817C /* ComposeViewModel+PublishState.swift */,
|
||||
|
@ -1131,14 +1131,14 @@
|
|||
path = Compose;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB789A2125F9F76D0071ACA0 /* TableViewCell */ = {
|
||||
DB789A2125F9F76D0071ACA0 /* CollectionViewCell */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.swift */,
|
||||
DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift */,
|
||||
DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentCollectionViewCell.swift */,
|
||||
DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift */,
|
||||
DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentTableViewCell.swift */,
|
||||
);
|
||||
path = TableViewCell;
|
||||
path = CollectionViewCell;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DB89B9EF25C10FD0008580ED /* CoreDataStack */ = {
|
||||
|
@ -1842,7 +1842,7 @@
|
|||
DB47229725F9EFAD00DA7F53 /* NSManagedObjectContext.swift in Sources */,
|
||||
2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */,
|
||||
DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */,
|
||||
DB789A1C25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift in Sources */,
|
||||
DB789A1C25F9F76A0071ACA0 /* ComposeStatusContentCollectionViewCell.swift in Sources */,
|
||||
DB1FD43625F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift in Sources */,
|
||||
DB9A488426034BD7008B817C /* APIService+Status.swift in Sources */,
|
||||
2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */,
|
||||
|
@ -1922,7 +1922,7 @@
|
|||
5DF1057925F88A1D00D6C0D4 /* PlayerContainerView.swift in Sources */,
|
||||
DB45FB0F25CA87D0005A8AC7 /* AuthenticationService.swift in Sources */,
|
||||
DBCCC71E25F73297007E1AB6 /* APIService+Reblog.swift in Sources */,
|
||||
DB789A2B25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.swift in Sources */,
|
||||
DB789A2B25F9F7AB0071ACA0 /* ComposeRepliedToTootContentCollectionViewCell.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -26,22 +26,22 @@ extension ComposeStatusSection {
|
|||
}
|
||||
|
||||
extension ComposeStatusSection {
|
||||
static func tableViewDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
|
||||
static func collectionViewDiffableDataSource(
|
||||
for collectionView: UICollectionView,
|
||||
dependency: NeedsDependency,
|
||||
managedObjectContext: NSManagedObjectContext,
|
||||
composeKind: ComposeKind,
|
||||
textEditorViewTextAttributesDelegate: TextEditorViewTextAttributesDelegate,
|
||||
composeStatusAttachmentTableViewCellDelegate: ComposeStatusAttachmentTableViewCellDelegate
|
||||
) -> UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem> {
|
||||
UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>(tableView: tableView) { [weak textEditorViewTextAttributesDelegate, weak composeStatusAttachmentTableViewCellDelegate] tableView, indexPath, item -> UITableViewCell? in
|
||||
composeStatusAttachmentTableViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate
|
||||
) -> UICollectionViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem> {
|
||||
UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, item -> UICollectionViewCell? in
|
||||
switch item {
|
||||
case .replyTo(let repliedToStatusObjectID):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeRepliedToTootContentTableViewCell.self), for: indexPath) as! ComposeRepliedToTootContentTableViewCell
|
||||
// TODO:
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeRepliedToTootContentCollectionViewCell.self), for: indexPath) as! ComposeRepliedToTootContentCollectionViewCell
|
||||
return cell
|
||||
case .input(let replyToTootObjectID, let attribute):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeStatusContentTableViewCell.self), for: indexPath) as! ComposeStatusContentTableViewCell
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusContentCollectionViewCell.self), for: indexPath) as! ComposeStatusContentCollectionViewCell
|
||||
cell.textEditorView.text = attribute.composeContent.value ?? ""
|
||||
managedObjectContext.perform {
|
||||
guard let replyToTootObjectID = replyToTootObjectID,
|
||||
|
@ -59,24 +59,24 @@ extension ComposeStatusSection {
|
|||
.removeDuplicates()
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { text in
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
collectionView.collectionViewLayout.invalidateLayout()
|
||||
// bind input data
|
||||
attribute.composeContent.value = text
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
return cell
|
||||
case .attachment(let attachmentService):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeStatusAttachmentTableViewCell.self), for: indexPath) as! ComposeStatusAttachmentTableViewCell
|
||||
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self), for: indexPath) as! ComposeStatusAttachmentCollectionViewCell
|
||||
cell.attachmentContainerView.descriptionTextView.text = attachmentService.description.value
|
||||
cell.delegate = composeStatusAttachmentTableViewCellDelegate
|
||||
attachmentService.imageData
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { imageData in
|
||||
let size = cell.attachmentContainerView.previewImageView.frame.size != .zero ? cell.attachmentContainerView.previewImageView.frame.size : CGSize(width: 1, height: 1)
|
||||
guard let imageData = imageData,
|
||||
let image = UIImage(data: imageData) else {
|
||||
let placeholder = UIImage.placeholder(
|
||||
size: cell.attachmentContainerView.previewImageView.frame.size,
|
||||
size: size,
|
||||
color: Asset.Colors.Background.systemGroupedBackground.color
|
||||
)
|
||||
.af.imageRounded(
|
||||
|
@ -86,7 +86,7 @@ extension ComposeStatusSection {
|
|||
return
|
||||
}
|
||||
cell.attachmentContainerView.previewImageView.image = image
|
||||
.af.imageAspectScaled(toFill: cell.attachmentContainerView.previewImageView.frame.size)
|
||||
.af.imageAspectScaled(toFill: size)
|
||||
.af.imageRounded(withCornerRadius: AttachmentContainerView.containerViewCornerRadius)
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
@ -97,6 +97,7 @@ extension ComposeStatusSection {
|
|||
.receive(on: DispatchQueue.main)
|
||||
.sink { uploadState, error in
|
||||
cell.attachmentContainerView.emptyStateView.isHidden = error == nil
|
||||
cell.attachmentContainerView.descriptionBackgroundView.isHidden = error != nil
|
||||
if let _ = error {
|
||||
cell.attachmentContainerView.activityIndicatorView.stopAnimating()
|
||||
} else {
|
||||
|
@ -130,7 +131,7 @@ extension ComposeStatusSection {
|
|||
|
||||
extension ComposeStatusSection {
|
||||
static func configure(
|
||||
cell: ComposeStatusContentTableViewCell,
|
||||
cell: ComposeStatusContentCollectionViewCell,
|
||||
attribute: ComposeStatusItem.ComposeStatusAttribute
|
||||
) {
|
||||
// set avatar
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
//
|
||||
// ComposeRepliedToTootContentCollectionViewCell.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-11.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class ComposeRepliedToTootContentCollectionViewCell: UICollectionViewCell {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ComposeRepliedToTootContentCollectionViewCell {
|
||||
|
||||
private func _init() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,18 +9,18 @@ import os.log
|
|||
import UIKit
|
||||
import Combine
|
||||
|
||||
protocol ComposeStatusAttachmentTableViewCellDelegate: class {
|
||||
func composeStatusAttachmentTableViewCell(_ cell: ComposeStatusAttachmentTableViewCell, removeButtonDidPressed button: UIButton)
|
||||
protocol ComposeStatusAttachmentCollectionViewCellDelegate: class {
|
||||
func composeStatusAttachmentCollectionViewCell(_ cell: ComposeStatusAttachmentCollectionViewCell, removeButtonDidPressed button: UIButton)
|
||||
}
|
||||
|
||||
final class ComposeStatusAttachmentTableViewCell: UITableViewCell {
|
||||
final class ComposeStatusAttachmentCollectionViewCell: UICollectionViewCell {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
static let verticalMarginHeight: CGFloat = ComposeStatusAttachmentTableViewCell.removeButtonSize.height * 0.5
|
||||
static let verticalMarginHeight: CGFloat = ComposeStatusAttachmentCollectionViewCell.removeButtonSize.height * 0.5
|
||||
static let removeButtonSize = CGSize(width: 22, height: 22)
|
||||
|
||||
weak var delegate: ComposeStatusAttachmentTableViewCellDelegate?
|
||||
weak var delegate: ComposeStatusAttachmentCollectionViewCellDelegate?
|
||||
|
||||
let attachmentContainerView = AttachmentContainerView()
|
||||
let removeButton: UIButton = {
|
||||
|
@ -31,7 +31,7 @@ final class ComposeStatusAttachmentTableViewCell: UITableViewCell {
|
|||
button.setImage(image, for: .normal)
|
||||
button.setBackgroundImage(.placeholder(color: Asset.Colors.Background.danger.color), for: .normal)
|
||||
button.layer.masksToBounds = true
|
||||
button.layer.cornerRadius = ComposeStatusAttachmentTableViewCell.removeButtonSize.width * 0.5
|
||||
button.layer.cornerRadius = ComposeStatusAttachmentCollectionViewCell.removeButtonSize.width * 0.5
|
||||
button.layer.borderColor = Asset.Colors.Background.dangerBorder.color.cgColor
|
||||
button.layer.borderWidth = 1
|
||||
return button
|
||||
|
@ -41,11 +41,14 @@ final class ComposeStatusAttachmentTableViewCell: UITableViewCell {
|
|||
super.prepareForReuse()
|
||||
|
||||
attachmentContainerView.activityIndicatorView.startAnimating()
|
||||
attachmentContainerView.previewImageView.af.cancelImageRequest()
|
||||
attachmentContainerView.previewImageView.image = .placeholder(color: .systemFill)
|
||||
delegate = nil
|
||||
disposeBag.removeAll()
|
||||
}
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
|
@ -56,18 +59,18 @@ final class ComposeStatusAttachmentTableViewCell: UITableViewCell {
|
|||
|
||||
}
|
||||
|
||||
extension ComposeStatusAttachmentTableViewCell {
|
||||
extension ComposeStatusAttachmentCollectionViewCell {
|
||||
|
||||
private func _init() {
|
||||
selectionStyle = .none
|
||||
// selectionStyle = .none
|
||||
|
||||
attachmentContainerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(attachmentContainerView)
|
||||
NSLayoutConstraint.activate([
|
||||
attachmentContainerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: ComposeStatusAttachmentTableViewCell.verticalMarginHeight),
|
||||
attachmentContainerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: ComposeStatusAttachmentCollectionViewCell.verticalMarginHeight),
|
||||
attachmentContainerView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor),
|
||||
attachmentContainerView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor),
|
||||
contentView.bottomAnchor.constraint(equalTo: attachmentContainerView.bottomAnchor, constant: ComposeStatusAttachmentTableViewCell.verticalMarginHeight),
|
||||
contentView.bottomAnchor.constraint(equalTo: attachmentContainerView.bottomAnchor, constant: ComposeStatusAttachmentCollectionViewCell.verticalMarginHeight),
|
||||
attachmentContainerView.heightAnchor.constraint(equalToConstant: 205).priority(.defaultHigh),
|
||||
])
|
||||
|
||||
|
@ -76,21 +79,21 @@ extension ComposeStatusAttachmentTableViewCell {
|
|||
NSLayoutConstraint.activate([
|
||||
removeButton.centerXAnchor.constraint(equalTo: attachmentContainerView.trailingAnchor),
|
||||
removeButton.centerYAnchor.constraint(equalTo: attachmentContainerView.topAnchor),
|
||||
removeButton.widthAnchor.constraint(equalToConstant: ComposeStatusAttachmentTableViewCell.removeButtonSize.width).priority(.defaultHigh),
|
||||
removeButton.heightAnchor.constraint(equalToConstant: ComposeStatusAttachmentTableViewCell.removeButtonSize.height).priority(.defaultHigh),
|
||||
removeButton.widthAnchor.constraint(equalToConstant: ComposeStatusAttachmentCollectionViewCell.removeButtonSize.width).priority(.defaultHigh),
|
||||
removeButton.heightAnchor.constraint(equalToConstant: ComposeStatusAttachmentCollectionViewCell.removeButtonSize.height).priority(.defaultHigh),
|
||||
])
|
||||
|
||||
removeButton.addTarget(self, action: #selector(ComposeStatusAttachmentTableViewCell.removeButtonDidPressed(_:)), for: .touchUpInside)
|
||||
removeButton.addTarget(self, action: #selector(ComposeStatusAttachmentCollectionViewCell.removeButtonDidPressed(_:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
extension ComposeStatusAttachmentTableViewCell {
|
||||
extension ComposeStatusAttachmentCollectionViewCell {
|
||||
|
||||
@objc private func removeButtonDidPressed(_ sender: UIButton) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
delegate?.composeStatusAttachmentTableViewCell(self, removeButtonDidPressed: sender)
|
||||
delegate?.composeStatusAttachmentCollectionViewCell(self, removeButtonDidPressed: sender)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ComposeStatusContentTableViewCell.swift
|
||||
// ComposeStatusContentCollectionViewCell.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-11.
|
||||
|
@ -9,7 +9,7 @@ import UIKit
|
|||
import Combine
|
||||
import TwitterTextEditor
|
||||
|
||||
final class ComposeStatusContentTableViewCell: UITableViewCell {
|
||||
final class ComposeStatusContentCollectionViewCell: UICollectionViewCell {
|
||||
|
||||
var disposeBag = Set<AnyCancellable>()
|
||||
|
||||
|
@ -27,8 +27,8 @@ final class ComposeStatusContentTableViewCell: UITableViewCell {
|
|||
|
||||
let composeContent = PassthroughSubject<String, Never>()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
_init()
|
||||
}
|
||||
|
||||
|
@ -39,10 +39,11 @@ final class ComposeStatusContentTableViewCell: UITableViewCell {
|
|||
|
||||
}
|
||||
|
||||
extension ComposeStatusContentTableViewCell {
|
||||
extension ComposeStatusContentCollectionViewCell {
|
||||
|
||||
private func _init() {
|
||||
selectionStyle = .none
|
||||
// selectionStyle = .none
|
||||
preservesSuperviewLayoutMargins = true
|
||||
|
||||
statusView.translatesAutoresizingMaskIntoConstraints = false
|
||||
contentView.addSubview(statusView)
|
||||
|
@ -82,12 +83,8 @@ extension ComposeStatusContentTableViewCell {
|
|||
|
||||
}
|
||||
|
||||
extension ComposeStatusContentTableViewCell {
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UITextViewDelegate
|
||||
extension ComposeStatusContentTableViewCell: TextEditorViewChangeObserver {
|
||||
extension ComposeStatusContentCollectionViewCell: TextEditorViewChangeObserver {
|
||||
func textEditorView(_ textEditorView: TextEditorView, didChangeWithChangeResult changeResult: TextEditorViewChangeResult) {
|
||||
guard changeResult.isTextChanged else { return }
|
||||
composeContent.send(textEditorView.text)
|
|
@ -39,15 +39,14 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
|||
return barButtonItem
|
||||
}()
|
||||
|
||||
let tableView: UITableView = {
|
||||
let tableView = ControlContainableTableView()
|
||||
tableView.register(ComposeRepliedToTootContentTableViewCell.self, forCellReuseIdentifier: String(describing: ComposeRepliedToTootContentTableViewCell.self))
|
||||
tableView.register(ComposeStatusContentTableViewCell.self, forCellReuseIdentifier: String(describing: ComposeStatusContentTableViewCell.self))
|
||||
tableView.register(ComposeStatusAttachmentTableViewCell.self, forCellReuseIdentifier: String(describing: ComposeStatusAttachmentTableViewCell.self))
|
||||
tableView.rowHeight = UITableView.automaticDimension
|
||||
tableView.separatorStyle = .none
|
||||
tableView.showsVerticalScrollIndicator = false
|
||||
return tableView
|
||||
let collectionView: UICollectionView = {
|
||||
let collectionViewLayout = ComposeViewController.createLayout()
|
||||
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: collectionViewLayout)
|
||||
collectionView.register(ComposeRepliedToTootContentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeRepliedToTootContentCollectionViewCell.self))
|
||||
collectionView.register(ComposeStatusContentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusContentCollectionViewCell.self))
|
||||
collectionView.register(ComposeStatusAttachmentCollectionViewCell.self, forCellWithReuseIdentifier: String(describing: ComposeStatusAttachmentCollectionViewCell.self))
|
||||
collectionView.backgroundColor = Asset.Colors.Background.systemBackground.color
|
||||
return collectionView
|
||||
}()
|
||||
|
||||
let composeToolbarView: ComposeToolbarView = {
|
||||
|
@ -86,6 +85,20 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
|||
|
||||
}
|
||||
|
||||
extension ComposeViewController {
|
||||
private static func createLayout() -> UICollectionViewLayout {
|
||||
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100))
|
||||
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(100))
|
||||
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
|
||||
let section = NSCollectionLayoutSection(group: group)
|
||||
section.contentInsetsReference = .readableContent
|
||||
// section.interGroupSpacing = 10
|
||||
// section.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 10, bottom: 10, trailing: 10)
|
||||
return UICollectionViewCompositionalLayout(section: section)
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
@ -103,13 +116,13 @@ extension ComposeViewController {
|
|||
navigationItem.rightBarButtonItem = publishBarButtonItem
|
||||
publishButton.addTarget(self, action: #selector(ComposeViewController.publishBarButtonItemPressed(_:)), for: .touchUpInside)
|
||||
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(tableView)
|
||||
collectionView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(collectionView)
|
||||
NSLayoutConstraint.activate([
|
||||
tableView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
collectionView.topAnchor.constraint(equalTo: view.topAnchor),
|
||||
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
||||
])
|
||||
|
||||
composeToolbarView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -133,9 +146,11 @@ extension ComposeViewController {
|
|||
view.bottomAnchor.constraint(equalTo: composeToolbarBackgroundView.bottomAnchor),
|
||||
])
|
||||
|
||||
tableView.delegate = self
|
||||
collectionView.delegate = self
|
||||
let longPressReorderGesture = UILongPressGestureRecognizer(target: self, action: #selector(ComposeViewController.longPressReorderGestureHandler(_:)))
|
||||
collectionView.addGestureRecognizer(longPressReorderGesture)
|
||||
viewModel.setupDiffableDataSource(
|
||||
for: tableView,
|
||||
for: collectionView,
|
||||
dependency: self,
|
||||
textEditorViewTextAttributesDelegate: self,
|
||||
composeStatusAttachmentTableViewCellDelegate: self
|
||||
|
@ -151,45 +166,45 @@ extension ComposeViewController {
|
|||
)
|
||||
.sink(receiveValue: { [weak self] isShow, state, endFrame in
|
||||
guard let self = self else { return }
|
||||
|
||||
|
||||
guard isShow, state == .dock else {
|
||||
self.tableView.contentInset.bottom = 0.0
|
||||
self.tableView.verticalScrollIndicatorInsets.bottom = 0.0
|
||||
self.collectionView.contentInset.bottom = self.view.safeAreaInsets.bottom
|
||||
self.collectionView.verticalScrollIndicatorInsets.bottom = self.view.safeAreaInsets.bottom
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.composeToolbarViewBottomLayoutConstraint.constant = 0.0
|
||||
self.composeToolbarViewBottomLayoutConstraint.constant = self.view.safeAreaInsets.bottom
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// isShow AND dock state
|
||||
let contentFrame = self.view.convert(self.tableView.frame, to: nil)
|
||||
let contentFrame = self.view.convert(self.collectionView.frame, to: nil)
|
||||
let padding = contentFrame.maxY - endFrame.minY
|
||||
guard padding > 0 else {
|
||||
self.tableView.contentInset.bottom = 0.0
|
||||
self.tableView.verticalScrollIndicatorInsets.bottom = 0.0
|
||||
self.collectionView.contentInset.bottom = self.view.safeAreaInsets.bottom
|
||||
self.collectionView.verticalScrollIndicatorInsets.bottom = self.view.safeAreaInsets.bottom
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.composeToolbarViewBottomLayoutConstraint.constant = 0.0
|
||||
self.composeToolbarViewBottomLayoutConstraint.constant = self.view.safeAreaInsets.bottom
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// add 16pt margin
|
||||
self.tableView.contentInset.bottom = padding + 16
|
||||
self.tableView.verticalScrollIndicatorInsets.bottom = padding + 16
|
||||
self.collectionView.contentInset.bottom = padding + 16
|
||||
self.collectionView.verticalScrollIndicatorInsets.bottom = padding + 16
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.composeToolbarViewBottomLayoutConstraint.constant = padding
|
||||
self.view.layoutIfNeeded()
|
||||
}
|
||||
})
|
||||
.store(in: &disposeBag)
|
||||
|
||||
|
||||
viewModel.isPublishBarButtonItemEnabled
|
||||
.receive(on: DispatchQueue.main)
|
||||
.assign(to: \.isEnabled, on: publishBarButtonItem)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
|
||||
// bind custom emojis
|
||||
viewModel.customEmojiViewModel
|
||||
.compactMap { $0?.emojis }
|
||||
|
@ -203,7 +218,7 @@ extension ComposeViewController {
|
|||
self.textEditorView()?.setNeedsUpdateTextAttributes()
|
||||
})
|
||||
.store(in: &disposeBag)
|
||||
|
||||
|
||||
// bind image picker toolbar state
|
||||
viewModel.attachmentServices
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
@ -236,7 +251,7 @@ extension ComposeViewController {
|
|||
switch item {
|
||||
case .input:
|
||||
guard let indexPath = diffableDataSource.indexPath(for: item),
|
||||
let cell = tableView.cellForRow(at: indexPath) as? ComposeStatusContentTableViewCell else {
|
||||
let cell = collectionView.cellForItem(at: indexPath) as? ComposeStatusContentCollectionViewCell else {
|
||||
continue
|
||||
}
|
||||
return cell.textEditorView
|
||||
|
@ -306,6 +321,33 @@ extension ComposeViewController {
|
|||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@objc private func longPressReorderGestureHandler(_ sender: UILongPressGestureRecognizer) {
|
||||
switch(sender.state) {
|
||||
case .began:
|
||||
guard let selectedIndexPath = collectionView.indexPathForItem(at: sender.location(in: collectionView)) else {
|
||||
break
|
||||
}
|
||||
collectionView.beginInteractiveMovementForItem(at: selectedIndexPath)
|
||||
case .changed:
|
||||
guard let selectedIndexPath = collectionView.indexPathForItem(at: sender.location(in: collectionView)),
|
||||
let diffableDataSource = viewModel.diffableDataSource else {
|
||||
break
|
||||
}
|
||||
guard let item = diffableDataSource.itemIdentifier(for: selectedIndexPath),
|
||||
case .attachment = item else {
|
||||
collectionView.cancelInteractiveMovement()
|
||||
return
|
||||
}
|
||||
|
||||
collectionView.updateInteractiveMovementTargetPosition(sender.location(in: collectionView))
|
||||
case .ended:
|
||||
collectionView.endInteractiveMovement()
|
||||
default:
|
||||
collectionView.cancelInteractiveMovement()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - TextEditorViewTextAttributesDelegate
|
||||
|
@ -476,10 +518,8 @@ extension ComposeViewController: ComposeToolbarViewDelegate {
|
|||
}
|
||||
|
||||
// MARK: - UITableViewDelegate
|
||||
extension ComposeViewController: UITableViewDelegate {
|
||||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return UITableView.automaticDimension
|
||||
}
|
||||
extension ComposeViewController: UICollectionViewDelegate {
|
||||
|
||||
}
|
||||
|
||||
// MARK: - UIAdaptivePresentationControllerDelegate
|
||||
|
@ -565,11 +605,11 @@ extension ComposeViewController: UIDocumentPickerDelegate {
|
|||
}
|
||||
|
||||
// MARK: - ComposeStatusAttachmentTableViewCellDelegate
|
||||
extension ComposeViewController: ComposeStatusAttachmentTableViewCellDelegate {
|
||||
extension ComposeViewController: ComposeStatusAttachmentCollectionViewCellDelegate {
|
||||
|
||||
func composeStatusAttachmentTableViewCell(_ cell: ComposeStatusAttachmentTableViewCell, removeButtonDidPressed button: UIButton) {
|
||||
func composeStatusAttachmentCollectionViewCell(_ cell: ComposeStatusAttachmentCollectionViewCell, removeButtonDidPressed button: UIButton) {
|
||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
||||
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
||||
guard let indexPath = collectionView.indexPath(for: cell) else { return }
|
||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||
guard case let .attachment(attachmentService) = item else { return }
|
||||
|
||||
|
|
|
@ -11,13 +11,13 @@ import TwitterTextEditor
|
|||
extension ComposeViewModel {
|
||||
|
||||
func setupDiffableDataSource(
|
||||
for tableView: UITableView,
|
||||
for collectionView: UICollectionView,
|
||||
dependency: NeedsDependency,
|
||||
textEditorViewTextAttributesDelegate: TextEditorViewTextAttributesDelegate,
|
||||
composeStatusAttachmentTableViewCellDelegate: ComposeStatusAttachmentTableViewCellDelegate
|
||||
composeStatusAttachmentTableViewCellDelegate: ComposeStatusAttachmentCollectionViewCellDelegate
|
||||
) {
|
||||
diffableDataSource = ComposeStatusSection.tableViewDiffableDataSource(
|
||||
for: tableView,
|
||||
let diffableDataSource = ComposeStatusSection.collectionViewDiffableDataSource(
|
||||
for: collectionView,
|
||||
dependency: dependency,
|
||||
managedObjectContext: context.managedObjectContext,
|
||||
composeKind: composeKind,
|
||||
|
@ -25,6 +25,26 @@ extension ComposeViewModel {
|
|||
composeStatusAttachmentTableViewCellDelegate: composeStatusAttachmentTableViewCellDelegate
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<ComposeStatusSection, ComposeStatusItem>()
|
||||
snapshot.appendSections([.repliedTo, .status, .attachment])
|
||||
switch composeKind {
|
||||
|
|
|
@ -23,7 +23,8 @@ final class ComposeViewModel {
|
|||
let activeAuthenticationBox: CurrentValueSubject<AuthenticationService.MastodonAuthenticationBox?, Never>
|
||||
|
||||
// output
|
||||
var diffableDataSource: UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>!
|
||||
//var diffableDataSource: UITableViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>!
|
||||
var diffableDataSource: UICollectionViewDiffableDataSource<ComposeStatusSection, ComposeStatusItem>!
|
||||
private(set) lazy var publishStateMachine: GKStateMachine = {
|
||||
// exclude timeline middle fetcher state
|
||||
let stateMachine = GKStateMachine(states: [
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
//
|
||||
// ComposeRepliedToTootContentTableViewCell.swift
|
||||
// Mastodon
|
||||
//
|
||||
// Created by MainasuK Cirno on 2021-3-11.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
final class ComposeRepliedToTootContentTableViewCell: UITableViewCell {
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
_init()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
_init()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ComposeRepliedToTootContentTableViewCell {
|
||||
|
||||
private func _init() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -76,10 +76,10 @@ extension MastodonAttachmentService.UploadState {
|
|||
service.error.send(error)
|
||||
case .finished:
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: upload attachment success", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
|
||||
break
|
||||
}
|
||||
} receiveValue: { response in
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: upload attachment %s success: %s", ((#file as NSString).lastPathComponent), #line, #function, response.value.id, response.value.url)
|
||||
service.attachment.value = response.value
|
||||
stateMachine.enter(Finish.self)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue