diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index 26e99547e..5afd4f65d 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -171,14 +171,17 @@ DB68A04A25E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68A04925E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift */; }; DB68A05D25E9055900CFDF14 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = DB68A05C25E9055900CFDF14 /* Settings.bundle */; }; DB68A06325E905E000CFDF14 /* UIApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB68A06225E905E000CFDF14 /* UIApplication.swift */; }; + DB6B35182601FA3400DC1E11 /* MastodonAttachmentService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B35172601FA3400DC1E11 /* MastodonAttachmentService.swift */; }; + DB6B351E2601FAEE00DC1E11 /* ComposeStatusAttachmentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentTableViewCell.swift */; }; DB6C8C0F25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */; }; DB71FD2C25F86A5100512AE1 /* AvatarStackContainerButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD2B25F86A5100512AE1 /* AvatarStackContainerButton.swift */; }; DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; }; 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 /* ComposeTootContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1B25F9F76A0071ACA0 /* ComposeTootContentTableViewCell.swift */; }; + DB789A1C25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift */; }; DB789A2B25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.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 */; }; DB89BA0025C10FD0008580ED /* CoreDataStack.h in Headers */ = {isa = PBXBuildFile; fileRef = DB89B9F025C10FD0008580ED /* CoreDataStack.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -450,14 +453,17 @@ DB68A04925E9027700CFDF14 /* DarkContentStatusBarStyleNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarkContentStatusBarStyleNavigationController.swift; sourceTree = ""; }; DB68A05C25E9055900CFDF14 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; DB68A06225E905E000CFDF14 /* UIApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIApplication.swift; sourceTree = ""; }; + DB6B35172601FA3400DC1E11 /* MastodonAttachmentService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonAttachmentService.swift; sourceTree = ""; }; + DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusAttachmentTableViewCell.swift; sourceTree = ""; }; DB6C8C0E25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Entity+Error.swift"; sourceTree = ""; }; DB71FD2B25F86A5100512AE1 /* AvatarStackContainerButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarStackContainerButton.swift; sourceTree = ""; }; DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = ""; }; DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = ""; }; DB789A0A25F9F2950071ACA0 /* ComposeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewController.swift; sourceTree = ""; }; DB789A1125F9F2CC0071ACA0 /* ComposeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeViewModel.swift; sourceTree = ""; }; - DB789A1B25F9F76A0071ACA0 /* ComposeTootContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeTootContentTableViewCell.swift; sourceTree = ""; }; + DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeStatusContentTableViewCell.swift; sourceTree = ""; }; DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeRepliedToTootContentTableViewCell.swift; sourceTree = ""; }; + DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentContainerView.swift; sourceTree = ""; }; 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 = ""; }; DB89B9F125C10FD0008580ED /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -720,12 +726,13 @@ isa = PBXGroup; children = ( DB45FB0425CA87B4005A8AC7 /* APIService */, + DB49A61925FF327D00B98345 /* EmojiService */, DB45FB0E25CA87D0005A8AC7 /* AuthenticationService.swift */, DB4563BC25E11A24004DA0B9 /* KeyboardResponderService.swift */, 2D206B8B25F6015000143C56 /* AudioPlaybackService.swift */, 2DA6054625F716A2006356F9 /* PlaybackState.swift */, 5DF1054025F886D400D6C0D4 /* ViedeoPlaybackService.swift */, - DB49A61925FF327D00B98345 /* EmojiService */, + DB6B35172601FA3400DC1E11 /* MastodonAttachmentService.swift */, ); path = Service; sourceTree = ""; @@ -1053,6 +1060,7 @@ isa = PBXGroup; children = ( DBA0A11225FB3FC10079C110 /* ComposeToolbarView.swift */, + DB8190C52601FF0400020C08 /* AttachmentContainerView.swift */, ); path = View; sourceTree = ""; @@ -1108,7 +1116,8 @@ isa = PBXGroup; children = ( DB789A2A25F9F7AB0071ACA0 /* ComposeRepliedToTootContentTableViewCell.swift */, - DB789A1B25F9F76A0071ACA0 /* ComposeTootContentTableViewCell.swift */, + DB789A1B25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift */, + DB6B351D2601FAEE00DC1E11 /* ComposeStatusAttachmentTableViewCell.swift */, ); path = TableViewCell; sourceTree = ""; @@ -1715,6 +1724,7 @@ DB49A61425FF2C5600B98345 /* EmojiService.swift in Sources */, 2D7631B325C159F700929FB9 /* Item.swift in Sources */, 5DF1054125F886D400D6C0D4 /* ViedeoPlaybackService.swift in Sources */, + DB6B35182601FA3400DC1E11 /* MastodonAttachmentService.swift in Sources */, 0FB3D2F725E4C24D00AAD544 /* MastodonPickServerViewModel.swift in Sources */, 2D61335E25C1894B00CAE157 /* APIService.swift in Sources */, DB66729625F9F91600D60309 /* ComposeStatusSection.swift in Sources */, @@ -1756,6 +1766,7 @@ 0FAA101C25E10E760017CCDE /* UIFont.swift in Sources */, 2D38F1D525CD465300561493 /* HomeTimelineViewController.swift in Sources */, DB98338825C945ED00AD9700 /* Assets.swift in Sources */, + DB6B351E2601FAEE00DC1E11 /* ComposeStatusAttachmentTableViewCell.swift in Sources */, 2DA7D04425CA52B200804E11 /* TimelineLoaderTableViewCell.swift in Sources */, DB8AF52F25C13561002E6C99 /* DocumentStore.swift in Sources */, 2D206B7225F5D27F00143C56 /* AudioContainerView.swift in Sources */, @@ -1798,7 +1809,7 @@ DB47229725F9EFAD00DA7F53 /* NSManagedObjectContext.swift in Sources */, 2D42FF7E25C82218004A627A /* ActionToolBarContainer.swift in Sources */, DB0140A125C40C0600F9F3CF /* MastodonPinBasedAuthenticationViewController.swift in Sources */, - DB789A1C25F9F76A0071ACA0 /* ComposeTootContentTableViewCell.swift in Sources */, + DB789A1C25F9F76A0071ACA0 /* ComposeStatusContentTableViewCell.swift in Sources */, DB1FD43625F26899004CFCFC /* MastodonPickServerViewModel+LoadIndexedServerState.swift in Sources */, 2D939AE825EE1CF80076FA61 /* MastodonRegisterViewController+Avatar.swift in Sources */, DB1D186C25EF5BA7003F1F23 /* PollTableView.swift in Sources */, @@ -1859,6 +1870,7 @@ 2D76316525C14BD100929FB9 /* PublicTimelineViewController.swift in Sources */, 2D69CFF425CA9E2200C3A1B2 /* LoadMoreConfigurableTableViewContainer.swift in Sources */, DB427DD825BAA00100D1B89D /* SceneDelegate.swift in Sources */, + DB8190C62601FF0400020C08 /* AttachmentContainerView.swift in Sources */, DB5086AB25CC0BBB00C2C187 /* AvatarConfigurableView.swift in Sources */, DB0140AE25C40C7300F9F3CF /* MastodonPinBasedAuthenticationViewModel.swift in Sources */, 2D32EAAC25CB96DC00C9ED86 /* TimelineMiddleLoaderTableViewCell.swift in Sources */, diff --git a/Mastodon/Diffiable/Item/ComposeStatusItem.swift b/Mastodon/Diffiable/Item/ComposeStatusItem.swift index 79655b946..d49203c33 100644 --- a/Mastodon/Diffiable/Item/ComposeStatusItem.swift +++ b/Mastodon/Diffiable/Item/ComposeStatusItem.swift @@ -12,6 +12,7 @@ import CoreData enum ComposeStatusItem { case replyTo(statusObjectID: NSManagedObjectID) case input(replyToStatusObjectID: NSManagedObjectID?, attribute: ComposeStatusAttribute) + case attachment(attachmentService: MastodonAttachmentService) } extension ComposeStatusItem: Hashable { } diff --git a/Mastodon/Diffiable/Section/ComposeStatusSection.swift b/Mastodon/Diffiable/Section/ComposeStatusSection.swift index 835007dcc..f0d912ebb 100644 --- a/Mastodon/Diffiable/Section/ComposeStatusSection.swift +++ b/Mastodon/Diffiable/Section/ComposeStatusSection.swift @@ -14,6 +14,7 @@ import TwitterTextEditor enum ComposeStatusSection: Equatable, Hashable { case repliedTo case status + case attachment } extension ComposeStatusSection { @@ -38,7 +39,7 @@ extension ComposeStatusSection { // TODO: return cell case .input(let replyToTootObjectID, let attribute): - let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeTootContentTableViewCell.self), for: indexPath) as! ComposeTootContentTableViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeStatusContentTableViewCell.self), for: indexPath) as! ComposeStatusContentTableViewCell managedObjectContext.perform { guard let replyToTootObjectID = replyToTootObjectID, let replyTo = managedObjectContext.object(with: replyToTootObjectID) as? Toot else { @@ -59,6 +60,10 @@ extension ComposeStatusSection { } .store(in: &cell.disposeBag) return cell + case .attachment(let attachmentService): + let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: ComposeStatusAttachmentTableViewCell.self), for: indexPath) as! ComposeStatusAttachmentTableViewCell + + return cell } } } @@ -66,7 +71,7 @@ extension ComposeStatusSection { extension ComposeStatusSection { static func configure( - cell: ComposeTootContentTableViewCell, + cell: ComposeStatusContentTableViewCell, attribute: ComposeStatusItem.ComposeStatusAttribute ) { // set avatar diff --git a/Mastodon/Scene/Compose/ComposeViewController.swift b/Mastodon/Scene/Compose/ComposeViewController.swift index df04b8d23..c903eb06d 100644 --- a/Mastodon/Scene/Compose/ComposeViewController.swift +++ b/Mastodon/Scene/Compose/ComposeViewController.swift @@ -38,7 +38,8 @@ final class ComposeViewController: UIViewController, NeedsDependency { let tableView: UITableView = { let tableView = ControlContainableTableView() tableView.register(ComposeRepliedToTootContentTableViewCell.self, forCellReuseIdentifier: String(describing: ComposeRepliedToTootContentTableViewCell.self)) - tableView.register(ComposeTootContentTableViewCell.self, forCellReuseIdentifier: String(describing: ComposeTootContentTableViewCell.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 return tableView @@ -196,7 +197,7 @@ extension ComposeViewController { switch item { case .input: guard let indexPath = diffableDataSource.indexPath(for: item), - let cell = tableView.cellForRow(at: indexPath) as? ComposeTootContentTableViewCell else { + let cell = tableView.cellForRow(at: indexPath) as? ComposeStatusContentTableViewCell else { continue } return cell.textEditorView @@ -401,6 +402,8 @@ extension ComposeViewController: ComposeToolbarViewDelegate { func composeToolbarView(_ composeToolbarView: ComposeToolbarView, cameraButtonDidPressed sender: UIButton) { os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function) + let attachmentService = MastodonAttachmentService() + viewModel.attachmentServices.value = viewModel.attachmentServices.value + [attachmentService] } func composeToolbarView(_ composeToolbarView: ComposeToolbarView, gifButtonDidPressed sender: UIButton) { diff --git a/Mastodon/Scene/Compose/ComposeViewModel+Diffable.swift b/Mastodon/Scene/Compose/ComposeViewModel+Diffable.swift index a3a0515e6..b58300294 100644 --- a/Mastodon/Scene/Compose/ComposeViewModel+Diffable.swift +++ b/Mastodon/Scene/Compose/ComposeViewModel+Diffable.swift @@ -24,7 +24,7 @@ extension ComposeViewModel { ) var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections([.repliedTo, .status]) + snapshot.appendSections([.repliedTo, .status, .attachment]) switch composeKind { case .reply(let statusObjectID): snapshot.appendItems([.replyTo(statusObjectID: statusObjectID)], toSection: .repliedTo) diff --git a/Mastodon/Scene/Compose/ComposeViewModel.swift b/Mastodon/Scene/Compose/ComposeViewModel.swift index 743f385e5..83082d837 100644 --- a/Mastodon/Scene/Compose/ComposeViewModel.swift +++ b/Mastodon/Scene/Compose/ComposeViewModel.swift @@ -32,6 +32,9 @@ final class ComposeViewModel { // custom emojis let customEmojiViewModel = CurrentValueSubject(nil) + // attachment + let attachmentServices = CurrentValueSubject<[MastodonAttachmentService], Never>([]) + init( context: AppContext, @@ -101,6 +104,26 @@ final class ComposeViewModel { self.customEmojiViewModel.value = self.context.emojiService.dequeueCustomEmojiViewModel(for: domain) } .store(in: &disposeBag) + + // bind snapshot + attachmentServices + .receive(on: DispatchQueue.main) + .sink { [weak self] attachmentServices in + guard let self = self else { return } + guard let diffableDataSource = self.diffableDataSource else { return } + var snapshot = diffableDataSource.snapshot() + + snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .attachment)) + var items: [ComposeStatusItem] = [] + for attachmentService in attachmentServices { + let item = ComposeStatusItem.attachment(attachmentService: attachmentService) + items.append(item) + } + snapshot.appendItems(items, toSection: .attachment) + + diffableDataSource.apply(snapshot) + } + .store(in: &disposeBag) } } diff --git a/Mastodon/Scene/Compose/TableViewCell/ComposeStatusAttachmentTableViewCell.swift b/Mastodon/Scene/Compose/TableViewCell/ComposeStatusAttachmentTableViewCell.swift new file mode 100644 index 000000000..b63a24ebb --- /dev/null +++ b/Mastodon/Scene/Compose/TableViewCell/ComposeStatusAttachmentTableViewCell.swift @@ -0,0 +1,45 @@ +// +// ComposeStatusAttachmentTableViewCell.swift +// Mastodon +// +// Created by MainasuK Cirno on 2021-3-17. +// + +import UIKit + +final class ComposeStatusAttachmentTableViewCell: UITableViewCell { + + static let verticalMarginHeight: CGFloat = 8 + + let attachmentContainerView = AttachmentContainerView() + + 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() { + attachmentContainerView.translatesAutoresizingMaskIntoConstraints = false + contentView.addSubview(attachmentContainerView) + NSLayoutConstraint.activate([ + attachmentContainerView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: ComposeStatusAttachmentTableViewCell.verticalMarginHeight), + attachmentContainerView.leadingAnchor.constraint(equalTo: contentView.readableContentGuide.leadingAnchor), + attachmentContainerView.trailingAnchor.constraint(equalTo: contentView.readableContentGuide.trailingAnchor), + contentView.bottomAnchor.constraint(equalTo: attachmentContainerView.bottomAnchor, constant: ComposeStatusAttachmentTableViewCell.verticalMarginHeight), + attachmentContainerView.heightAnchor.constraint(equalToConstant: 205).priority(.defaultHigh), + ]) + + attachmentContainerView.attachmentPreviewImageView.backgroundColor = .systemFill + } + +} + diff --git a/Mastodon/Scene/Compose/TableViewCell/ComposeTootContentTableViewCell.swift b/Mastodon/Scene/Compose/TableViewCell/ComposeStatusContentTableViewCell.swift similarity index 84% rename from Mastodon/Scene/Compose/TableViewCell/ComposeTootContentTableViewCell.swift rename to Mastodon/Scene/Compose/TableViewCell/ComposeStatusContentTableViewCell.swift index 9f39f1989..f5f778946 100644 --- a/Mastodon/Scene/Compose/TableViewCell/ComposeTootContentTableViewCell.swift +++ b/Mastodon/Scene/Compose/TableViewCell/ComposeStatusContentTableViewCell.swift @@ -1,5 +1,5 @@ // -// ComposeTootContentTableViewCell.swift +// ComposeStatusContentTableViewCell.swift // Mastodon // // Created by MainasuK Cirno on 2021-3-11. @@ -9,7 +9,7 @@ import UIKit import Combine import TwitterTextEditor -final class ComposeTootContentTableViewCell: UITableViewCell { +final class ComposeStatusContentTableViewCell: UITableViewCell { var disposeBag = Set() @@ -39,7 +39,7 @@ final class ComposeTootContentTableViewCell: UITableViewCell { } -extension ComposeTootContentTableViewCell { +extension ComposeStatusContentTableViewCell { private func _init() { selectionStyle = .none @@ -56,6 +56,9 @@ extension ComposeTootContentTableViewCell { statusView.nameTrialingDotLabel.isHidden = true statusView.dateLabel.isHidden = true + statusView.setContentHuggingPriority(.defaultHigh, for: .vertical) + statusView.setContentCompressionResistancePriority(.required - 1, for: .vertical) + textEditorView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(textEditorView) NSLayoutConstraint.activate([ @@ -65,6 +68,7 @@ extension ComposeTootContentTableViewCell { contentView.bottomAnchor.constraint(equalTo: textEditorView.bottomAnchor, constant: 20), textEditorView.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).priority(.defaultHigh), ]) + textEditorView.setContentCompressionResistancePriority(.required - 2, for: .vertical) // TODO: @@ -78,12 +82,12 @@ extension ComposeTootContentTableViewCell { } -extension ComposeTootContentTableViewCell { +extension ComposeStatusContentTableViewCell { } // MARK: - UITextViewDelegate -extension ComposeTootContentTableViewCell: TextEditorViewChangeObserver { +extension ComposeStatusContentTableViewCell: TextEditorViewChangeObserver { func textEditorView(_ textEditorView: TextEditorView, didChangeWithChangeResult changeResult: TextEditorViewChangeResult) { guard changeResult.isTextChanged else { return } composeContent.send(textEditorView.text) diff --git a/Mastodon/Scene/Compose/View/AttachmentContainerView.swift b/Mastodon/Scene/Compose/View/AttachmentContainerView.swift new file mode 100644 index 000000000..f098d983c --- /dev/null +++ b/Mastodon/Scene/Compose/View/AttachmentContainerView.swift @@ -0,0 +1,48 @@ +// +// AttachmentContainerView.swift +// Mastodon +// +// Created by MainasuK Cirno on 2021-3-17. +// + +import UIKit + +final class AttachmentContainerView: UIView { + + let attachmentPreviewImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFill + imageView.layer.masksToBounds = true + imageView.layer.cornerRadius = 4 + imageView.layer.cornerCurve = .continuous + return imageView + }() + + override init(frame: CGRect) { + super.init(frame: frame) + _init() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + _init() + } + +} + +extension AttachmentContainerView { + + private func _init() { + + attachmentPreviewImageView.translatesAutoresizingMaskIntoConstraints = false + addSubview(attachmentPreviewImageView) + NSLayoutConstraint.activate([ + attachmentPreviewImageView.topAnchor.constraint(equalTo: topAnchor), + attachmentPreviewImageView.leadingAnchor.constraint(equalTo: leadingAnchor), + attachmentPreviewImageView.trailingAnchor.constraint(equalTo: trailingAnchor), + attachmentPreviewImageView.bottomAnchor.constraint(equalTo: bottomAnchor), + ]) + + } + +} diff --git a/Mastodon/Service/MastodonAttachmentService.swift b/Mastodon/Service/MastodonAttachmentService.swift new file mode 100644 index 000000000..bdf6da657 --- /dev/null +++ b/Mastodon/Service/MastodonAttachmentService.swift @@ -0,0 +1,27 @@ +// +// MastodonAttachmentService.swift +// Mastodon +// +// Created by MainasuK Cirno on 2021-3-17. +// + +import UIKit +import Combine + +final class MastodonAttachmentService { + + let identifier = UUID() + +} + +extension MastodonAttachmentService: Equatable, Hashable { + + static func == (lhs: MastodonAttachmentService, rhs: MastodonAttachmentService) -> Bool { + return lhs.identifier == rhs.identifier + } + + func hash(into hasher: inout Hasher) { + hasher.combine(identifier) + } + +}