mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 01:31:02 +00:00
Refactoring
This commit is contained in:
parent
b5e128a1b0
commit
4426f84df4
15 changed files with 142 additions and 379 deletions
|
@ -1,34 +0,0 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Mastodon
|
||||
import UIKit
|
||||
import ViewModels
|
||||
|
||||
final class CompositionAttachmentsDataSource: UICollectionViewDiffableDataSource<Int, Attachment> {
|
||||
private let updateQueue =
|
||||
DispatchQueue(label: "com.metabolist.metatext.composition-attachments-data-source.update-queue")
|
||||
|
||||
init(collectionView: UICollectionView,
|
||||
viewModelProvider: @escaping (IndexPath) -> (CompositionAttachmentViewModel, CompositionViewModel)) {
|
||||
let registration = UICollectionView.CellRegistration
|
||||
<CompositionAttachmentCollectionViewCell, (CompositionAttachmentViewModel, CompositionViewModel)> {
|
||||
$0.viewModel = $2.0
|
||||
$0.parentViewModel = $2.1
|
||||
}
|
||||
|
||||
super.init(collectionView: collectionView) { collectionView, indexPath, _ in
|
||||
collectionView.dequeueConfiguredReusableCell(
|
||||
using: registration,
|
||||
for: indexPath,
|
||||
item: viewModelProvider(indexPath))
|
||||
}
|
||||
}
|
||||
|
||||
override func apply(_ snapshot: NSDiffableDataSourceSnapshot<Int, Attachment>,
|
||||
animatingDifferences: Bool = true,
|
||||
completion: (() -> Void)? = nil) {
|
||||
updateQueue.async {
|
||||
super.apply(snapshot, animatingDifferences: animatingDifferences, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,10 +14,14 @@
|
|||
D007023E25562A2800F38136 /* ConversationAvatarsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D007023D25562A2800F38136 /* ConversationAvatarsView.swift */; };
|
||||
D0070252255921B100F38136 /* AccountFieldView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0070251255921B100F38136 /* AccountFieldView.swift */; };
|
||||
D00CB2ED2533ACC00080096B /* StatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00CB2EC2533ACC00080096B /* StatusView.swift */; };
|
||||
D015B13525A812DD006D88A8 /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41E224F8889700D55A2D /* AttachmentsView.swift */; };
|
||||
D015B13A25A812E6006D88A8 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */; };
|
||||
D015B13F25A812EC006D88A8 /* PlayerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FE1C8E253686F9003EF1EB /* PlayerView.swift */; };
|
||||
D015B14425A812F6006D88A8 /* PlayerCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0FE1C9725368A9D003EF1EB /* PlayerCache.swift */; };
|
||||
D01C6FAC252024BD003D0300 /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01C6FAB252024BD003D0300 /* Array+Extensions.swift */; };
|
||||
D01EF22425182B1F00650C6B /* AccountHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01EF22325182B1F00650C6B /* AccountHeaderView.swift */; };
|
||||
D01F41D924F880C400D55A2D /* TouchFallthroughTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */; };
|
||||
D01F41E424F8889700D55A2D /* StatusAttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41E224F8889700D55A2D /* StatusAttachmentsView.swift */; };
|
||||
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D01F41E224F8889700D55A2D /* AttachmentsView.swift */; };
|
||||
D02E1F95250B13210071AD56 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02E1F94250B13210071AD56 /* SafariView.swift */; };
|
||||
D036AA02254B6101009094DF /* NotificationListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA01254B6101009094DF /* NotificationListCell.swift */; };
|
||||
D036AA07254B6118009094DF /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D036AA06254B6118009094DF /* NotificationView.swift */; };
|
||||
|
@ -34,15 +38,7 @@
|
|||
D04F9E8E259E9C950081B0C9 /* ViewModels in Frameworks */ = {isa = PBXBuildFile; productRef = D04F9E8D259E9C950081B0C9 /* ViewModels */; };
|
||||
D0625E59250F092900502611 /* StatusListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E58250F092900502611 /* StatusListCell.swift */; };
|
||||
D0625E5D250F0B5C00502611 /* StatusContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E5C250F0B5C00502611 /* StatusContentConfiguration.swift */; };
|
||||
D065965B25899DAE0096AC5D /* CompositionAttachmentsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D065965A25899DAE0096AC5D /* CompositionAttachmentsDataSource.swift */; };
|
||||
D065966125899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D065966025899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift */; };
|
||||
D065966225899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D065966025899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift */; };
|
||||
D065966725899E910096AC5D /* CompositionAttachmentsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D065965A25899DAE0096AC5D /* CompositionAttachmentsDataSource.swift */; };
|
||||
D06BC5E625202AD90079541D /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06BC5E525202AD90079541D /* ProfileViewController.swift */; };
|
||||
D0804133258D902900AD6139 /* CompositionAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0804132258D902900AD6139 /* CompositionAttachmentView.swift */; };
|
||||
D0804134258D902900AD6139 /* CompositionAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0804132258D902900AD6139 /* CompositionAttachmentView.swift */; };
|
||||
D080413E258D904400AD6139 /* CompositionAttachmentContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D080413D258D904400AD6139 /* CompositionAttachmentContentConfiguration.swift */; };
|
||||
D080413F258D904400AD6139 /* CompositionAttachmentContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D080413D258D904400AD6139 /* CompositionAttachmentContentConfiguration.swift */; };
|
||||
D0849C7F25903C4900A5EBCC /* Status+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0849C7E25903C4900A5EBCC /* Status+Extensions.swift */; };
|
||||
D08B8D3D253F929E00B1EBEF /* ImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D3C253F929E00B1EBEF /* ImageViewController.swift */; };
|
||||
D08B8D42253F92B600B1EBEF /* ImagePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D41253F92B600B1EBEF /* ImagePageViewController.swift */; };
|
||||
|
@ -69,7 +65,7 @@
|
|||
D0B32F50250B373600311912 /* RegistrationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B32F4F250B373600311912 /* RegistrationView.swift */; };
|
||||
D0B5FE9B251583DB00478838 /* ProfileCollection+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */; };
|
||||
D0B8510C25259E56004E0744 /* LoadMoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B8510B25259E56004E0744 /* LoadMoreCell.swift */; };
|
||||
D0BEB1F324F8EE8C001B0F04 /* StatusAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F224F8EE8C001B0F04 /* StatusAttachmentView.swift */; };
|
||||
D0BEB1F324F8EE8C001B0F04 /* AttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */; };
|
||||
D0BEB1F724F9A84B001B0F04 /* LoadingTableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1F624F9A84B001B0F04 /* LoadingTableFooterView.swift */; };
|
||||
D0BEB1FF24F9E5BB001B0F04 /* ListsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB1FE24F9E5BB001B0F04 /* ListsView.swift */; };
|
||||
D0BEB20524FA1107001B0F04 /* FiltersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0BEB20424FA1107001B0F04 /* FiltersView.swift */; };
|
||||
|
@ -176,7 +172,7 @@
|
|||
D01C6FAB252024BD003D0300 /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = "<group>"; };
|
||||
D01EF22325182B1F00650C6B /* AccountHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountHeaderView.swift; sourceTree = "<group>"; };
|
||||
D01F41D624F880C400D55A2D /* TouchFallthroughTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TouchFallthroughTextView.swift; sourceTree = "<group>"; };
|
||||
D01F41E224F8889700D55A2D /* StatusAttachmentsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusAttachmentsView.swift; sourceTree = "<group>"; };
|
||||
D01F41E224F8889700D55A2D /* AttachmentsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttachmentsView.swift; sourceTree = "<group>"; };
|
||||
D02E1F94250B13210071AD56 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
||||
D036AA01254B6101009094DF /* NotificationListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationListCell.swift; sourceTree = "<group>"; };
|
||||
D036AA06254B6118009094DF /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = "<group>"; };
|
||||
|
@ -187,13 +183,9 @@
|
|||
D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D0625E58250F092900502611 /* StatusListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusListCell.swift; sourceTree = "<group>"; };
|
||||
D0625E5C250F0B5C00502611 /* StatusContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentConfiguration.swift; sourceTree = "<group>"; };
|
||||
D065965A25899DAE0096AC5D /* CompositionAttachmentsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachmentsDataSource.swift; sourceTree = "<group>"; };
|
||||
D065966025899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachmentCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
D0666A2124C677B400F3F04B /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D0666A2524C677B400F3F04B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D06BC5E525202AD90079541D /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
|
||||
D0804132258D902900AD6139 /* CompositionAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachmentView.swift; sourceTree = "<group>"; };
|
||||
D080413D258D904400AD6139 /* CompositionAttachmentContentConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachmentContentConfiguration.swift; sourceTree = "<group>"; };
|
||||
D0849C7E25903C4900A5EBCC /* Status+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Status+Extensions.swift"; sourceTree = "<group>"; };
|
||||
D085C3BB25008DEC008A6C5E /* DB */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DB; sourceTree = "<group>"; };
|
||||
D08B8D3C253F929E00B1EBEF /* ImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -222,7 +214,7 @@
|
|||
D0B5FE9A251583DB00478838 /* ProfileCollection+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileCollection+Extensions.swift"; sourceTree = "<group>"; };
|
||||
D0B8510B25259E56004E0744 /* LoadMoreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadMoreCell.swift; sourceTree = "<group>"; };
|
||||
D0BDF66524FD7A6400C7FA1C /* ServiceLayer */ = {isa = PBXFileReference; lastKnownFileType = folder; path = ServiceLayer; sourceTree = "<group>"; };
|
||||
D0BEB1F224F8EE8C001B0F04 /* StatusAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusAttachmentView.swift; sourceTree = "<group>"; };
|
||||
D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentView.swift; sourceTree = "<group>"; };
|
||||
D0BEB1F624F9A84B001B0F04 /* LoadingTableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingTableFooterView.swift; sourceTree = "<group>"; };
|
||||
D0BEB1FE24F9E5BB001B0F04 /* ListsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListsView.swift; sourceTree = "<group>"; };
|
||||
D0BEB20424FA1107001B0F04 /* FiltersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersView.swift; sourceTree = "<group>"; };
|
||||
|
@ -366,8 +358,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D0EA593F2522AC8700804347 /* CardView.swift */,
|
||||
D01F41E224F8889700D55A2D /* StatusAttachmentsView.swift */,
|
||||
D0BEB1F224F8EE8C001B0F04 /* StatusAttachmentView.swift */,
|
||||
D036AA16254CA823009094DF /* StatusBodyView.swift */,
|
||||
D0625E5C250F0B5C00502611 /* StatusContentConfiguration.swift */,
|
||||
D0625E58250F092900502611 /* StatusListCell.swift */,
|
||||
|
@ -419,7 +409,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */,
|
||||
D065965A25899DAE0096AC5D /* CompositionAttachmentsDataSource.swift */,
|
||||
);
|
||||
path = "Data Sources";
|
||||
sourceTree = "<group>";
|
||||
|
@ -442,10 +431,9 @@
|
|||
D0F0B125251A90F400942152 /* AccountListCell.swift */,
|
||||
D0F0B10D251A868200942152 /* AccountView.swift */,
|
||||
D0C7D42424F76169001EBDBB /* AddIdentityView.swift */,
|
||||
D01F41E224F8889700D55A2D /* AttachmentsView.swift */,
|
||||
D0CE9F86258B076900E3A6B6 /* AttachmentUploadView.swift */,
|
||||
D065966025899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift */,
|
||||
D080413D258D904400AD6139 /* CompositionAttachmentContentConfiguration.swift */,
|
||||
D0804132258D902900AD6139 /* CompositionAttachmentView.swift */,
|
||||
D0BEB1F224F8EE8C001B0F04 /* AttachmentView.swift */,
|
||||
D0E9F9A9258450B300EF503D /* CompositionInputAccessoryView.swift */,
|
||||
D08E52ED257D757100FA2C5F /* CompositionView.swift */,
|
||||
D007023D25562A2800F38136 /* ConversationAvatarsView.swift */,
|
||||
|
@ -767,7 +755,6 @@
|
|||
D0C7D49C24F7616A001EBDBB /* RootView.swift in Sources */,
|
||||
D0F0B126251A90F400942152 /* AccountListCell.swift in Sources */,
|
||||
D0B32F50250B373600311912 /* RegistrationView.swift in Sources */,
|
||||
D0804133258D902900AD6139 /* CompositionAttachmentView.swift in Sources */,
|
||||
D08B8D612540DE3B00B1EBEF /* ZoomDismissalInteractionController.swift in Sources */,
|
||||
D036AA07254B6118009094DF /* NotificationView.swift in Sources */,
|
||||
D08E52EE257D757100FA2C5F /* CompositionView.swift in Sources */,
|
||||
|
@ -777,7 +764,7 @@
|
|||
D007023E25562A2800F38136 /* ConversationAvatarsView.swift in Sources */,
|
||||
D0E7AD3925870B13005F5E2D /* UIVIewController+Extensions.swift in Sources */,
|
||||
D0625E5D250F0B5C00502611 /* StatusContentConfiguration.swift in Sources */,
|
||||
D0BEB1F324F8EE8C001B0F04 /* StatusAttachmentView.swift in Sources */,
|
||||
D0BEB1F324F8EE8C001B0F04 /* AttachmentView.swift in Sources */,
|
||||
D0C7D49A24F7616A001EBDBB /* TableView.swift in Sources */,
|
||||
D08B8D622540DE3B00B1EBEF /* ZoomTransitionController.swift in Sources */,
|
||||
D0F0B12E251A97E400942152 /* TableViewController.swift in Sources */,
|
||||
|
@ -791,7 +778,6 @@
|
|||
D0C7D4D624F7616A001EBDBB /* NSMutableAttributedString+Extensions.swift in Sources */,
|
||||
D0E9F9AA258450B300EF503D /* CompositionInputAccessoryView.swift in Sources */,
|
||||
D0849C7F25903C4900A5EBCC /* Status+Extensions.swift in Sources */,
|
||||
D080413E258D904400AD6139 /* CompositionAttachmentContentConfiguration.swift in Sources */,
|
||||
D0625E59250F092900502611 /* StatusListCell.swift in Sources */,
|
||||
D0E569DB2529319100FA1D72 /* LoadMoreView.swift in Sources */,
|
||||
D0C7D49D24F7616A001EBDBB /* PostingReadingPreferencesView.swift in Sources */,
|
||||
|
@ -818,16 +804,14 @@
|
|||
D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */,
|
||||
D08E512125786A6600FA2C5F /* UIButton+Extensions.swift in Sources */,
|
||||
D0EA59482522B8B600804347 /* ViewConstants.swift in Sources */,
|
||||
D065965B25899DAE0096AC5D /* CompositionAttachmentsDataSource.swift in Sources */,
|
||||
D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */,
|
||||
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */,
|
||||
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
||||
D08B8D3D253F929E00B1EBEF /* ImageViewController.swift in Sources */,
|
||||
D0B8510C25259E56004E0744 /* LoadMoreCell.swift in Sources */,
|
||||
D08E52612579D2E100FA2C5F /* DomainBlocksView.swift in Sources */,
|
||||
D01F41E424F8889700D55A2D /* StatusAttachmentsView.swift in Sources */,
|
||||
D01F41E424F8889700D55A2D /* AttachmentsView.swift in Sources */,
|
||||
D00702312555F4AE00F38136 /* ConversationView.swift in Sources */,
|
||||
D065966125899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift in Sources */,
|
||||
D0BEB21124FA2A91001B0F04 /* EditFilterView.swift in Sources */,
|
||||
D08B8D4A253FC36500B1EBEF /* ImageNavigationController.swift in Sources */,
|
||||
D0070252255921B100F38136 /* AccountFieldView.swift in Sources */,
|
||||
|
@ -866,16 +850,16 @@
|
|||
D038273C259EA38F00056E0F /* NewStatusView.swift in Sources */,
|
||||
D08E52EF257D757100FA2C5F /* CompositionView.swift in Sources */,
|
||||
D0CE9F88258B076900E3A6B6 /* AttachmentUploadView.swift in Sources */,
|
||||
D080413F258D904400AD6139 /* CompositionAttachmentContentConfiguration.swift in Sources */,
|
||||
D08E52C7257C7AEE00FA2C5F /* ShareErrorViewController.swift in Sources */,
|
||||
D015B14425A812F6006D88A8 /* PlayerCache.swift in Sources */,
|
||||
D015B13F25A812EC006D88A8 /* PlayerView.swift in Sources */,
|
||||
D015B13A25A812E6006D88A8 /* AttachmentView.swift in Sources */,
|
||||
D08E52F8257D78BE00FA2C5F /* ViewConstants.swift in Sources */,
|
||||
D036EBC2259FE2AD00EC1CFC /* UIVIewController+Extensions.swift in Sources */,
|
||||
D065966725899E910096AC5D /* CompositionAttachmentsDataSource.swift in Sources */,
|
||||
D015B13525A812DD006D88A8 /* AttachmentsView.swift in Sources */,
|
||||
D0FCC106259C4E62000B67DF /* NewStatusViewController.swift in Sources */,
|
||||
D036EBBD259FE2A100EC1CFC /* Array+Extensions.swift in Sources */,
|
||||
D036EBB3259FE28800EC1CFC /* UIColor+Extensions.swift in Sources */,
|
||||
D0804134258D902900AD6139 /* CompositionAttachmentView.swift in Sources */,
|
||||
D065966225899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift in Sources */,
|
||||
D036EBB8259FE29800EC1CFC /* Status+Extensions.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -6,7 +6,6 @@ import SafariServices
|
|||
import SwiftUI
|
||||
import ViewModels
|
||||
|
||||
// swiftlint:disable file_length
|
||||
class TableViewController: UITableViewController {
|
||||
var transitionViewTag = -1
|
||||
|
||||
|
@ -16,6 +15,7 @@ class TableViewController: UITableViewController {
|
|||
private let webfingerIndicatorView = WebfingerIndicatorView()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var cellHeightCaches = [CGFloat: [CollectionItem: CGFloat]]()
|
||||
private var shouldKeepPlayingVideoAfterDismissal = false
|
||||
|
||||
private lazy var dataSource: TableViewDataSource = {
|
||||
.init(tableView: tableView, viewModelProvider: viewModel.viewModel(indexPath:))
|
||||
|
@ -59,18 +59,6 @@ class TableViewController: UITableViewController {
|
|||
viewModel.request(maxId: nil, minId: nil)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
updateAutoplayViews()
|
||||
}
|
||||
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
updateAutoplayViews()
|
||||
}
|
||||
|
||||
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
guard scrollView.isDragging else { return }
|
||||
|
||||
|
@ -79,8 +67,6 @@ class TableViewController: UITableViewController {
|
|||
for loadMoreView in visibleLoadMoreViews {
|
||||
loadMoreView.directionChanged(up: up)
|
||||
}
|
||||
|
||||
updateAutoplayViews()
|
||||
}
|
||||
|
||||
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
|
@ -124,10 +110,6 @@ class TableViewController: UITableViewController {
|
|||
}
|
||||
|
||||
extension TableViewController {
|
||||
static let autoplayableAttachmentsView = PassthroughSubject<StatusAttachmentsView?, Never>()
|
||||
static let autoplayableAttachmentsViewNotification =
|
||||
Notification.Name("com.metabolist.metatext.attachment-view-became-autoplayable")
|
||||
|
||||
func report(viewModel: ReportViewModel) {
|
||||
let reportViewController = ReportViewController(viewModel: viewModel)
|
||||
let navigationController = UINavigationController(rootViewController: reportViewController)
|
||||
|
@ -188,7 +170,12 @@ extension TableViewController: AVPlayerViewControllerDelegate {
|
|||
willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator) {
|
||||
try? AVAudioSession.sharedInstance().setCategory(.ambient, mode: .default)
|
||||
playerViewController.player?.isMuted = true
|
||||
updateAutoplayViews()
|
||||
|
||||
coordinator.animate(alongsideTransition: nil) { _ in
|
||||
if self.shouldKeepPlayingVideoAfterDismissal {
|
||||
playerViewController.player?.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,17 +254,6 @@ private extension TableViewController {
|
|||
.compactMap { [weak self] _ in self?.tableView.indexPathsForVisibleRows?.first }
|
||||
.sink { [weak self] in self?.viewModel.viewedAtTop(indexPath: $0) }
|
||||
.store(in: &cancellables)
|
||||
|
||||
Self.autoplayableAttachmentsView
|
||||
.removeDuplicates()
|
||||
.sink {
|
||||
let notification = Notification(
|
||||
name: Self.autoplayableAttachmentsViewNotification,
|
||||
object: $0,
|
||||
userInfo: nil)
|
||||
NotificationCenter.default.post(notification)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func update(_ update: CollectionUpdate) {
|
||||
|
@ -313,8 +289,6 @@ private extension TableViewController {
|
|||
self.tableView.contentOffset.y -= offsetFromNavigationBar
|
||||
}
|
||||
}
|
||||
|
||||
self.updateAutoplayViews()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,6 +343,8 @@ private extension TableViewController {
|
|||
playerViewController.delegate = self
|
||||
playerViewController.player = player
|
||||
|
||||
shouldKeepPlayingVideoAfterDismissal = attachmentViewModel.shouldAutoplay
|
||||
|
||||
present(playerViewController, animated: true) {
|
||||
try? AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
|
||||
player.isMuted = false
|
||||
|
@ -411,22 +387,4 @@ private extension TableViewController {
|
|||
|
||||
present(activityViewController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func updateAutoplayViews() {
|
||||
if let visibleView = navigationController?.visibleViewController?.view,
|
||||
view.isDescendant(of: visibleView),
|
||||
let superview = view.superview,
|
||||
let attachmentsViewClosestToCenter = tableView.visibleCells
|
||||
.compactMap({ ($0.contentView as? StatusView)?.bodyView.attachmentsView })
|
||||
.filter(\.shouldAutoplay)
|
||||
.min(by: {
|
||||
abs(superview.convert($0.frame, from: $0.superview).midY - view.frame.midY)
|
||||
< abs(superview.convert($1.frame, from: $1.superview).midY - view.frame.midY)
|
||||
}) {
|
||||
Self.autoplayableAttachmentsView.send(attachmentsViewClosestToCenter)
|
||||
} else {
|
||||
Self.autoplayableAttachmentsView.send(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
// swiftlint:enable file_length
|
||||
|
|
|
@ -4,22 +4,22 @@ import Foundation
|
|||
import Mastodon
|
||||
import Network
|
||||
|
||||
public struct AttachmentViewModel {
|
||||
public final class AttachmentViewModel: ObservableObject {
|
||||
public let attachment: Attachment
|
||||
|
||||
private let status: Status
|
||||
private let identification: Identification
|
||||
private let status: Status?
|
||||
|
||||
init(attachment: Attachment, status: Status, identification: Identification) {
|
||||
init(attachment: Attachment, identification: Identification, status: Status? = nil) {
|
||||
self.attachment = attachment
|
||||
self.status = status
|
||||
self.identification = identification
|
||||
self.status = status
|
||||
}
|
||||
}
|
||||
|
||||
public extension AttachmentViewModel {
|
||||
var tag: Int {
|
||||
attachment.id.appending(status.id).hashValue
|
||||
attachment.id.appending(status?.id ?? "").hashValue
|
||||
}
|
||||
|
||||
var shouldAutoplay: Bool {
|
||||
|
@ -38,5 +38,11 @@ public extension AttachmentViewModel {
|
|||
}
|
||||
|
||||
private extension AttachmentViewModel {
|
||||
static let wifiMonitor = NWPathMonitor(requiredInterfaceType: .wifi)
|
||||
static var wifiMonitor: NWPathMonitor = {
|
||||
let monitor = NWPathMonitor(requiredInterfaceType: .wifi)
|
||||
|
||||
monitor.start(queue: .main)
|
||||
|
||||
return monitor
|
||||
}()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol AttachmentsRenderingViewModel {
|
||||
var attachmentViewModels: [AttachmentViewModel] { get }
|
||||
var shouldShowAttachments: Bool { get }
|
||||
var shouldShowHideAttachmentsButton: Bool { get }
|
||||
var sensitive: Bool { get }
|
||||
var canRemoveAttachments: Bool { get }
|
||||
func attachmentSelected(viewModel: AttachmentViewModel)
|
||||
func removeAttachment(viewModel: AttachmentViewModel)
|
||||
func toggleShowAttachments()
|
||||
}
|
||||
|
||||
public extension AttachmentsRenderingViewModel {
|
||||
var shouldShowAttachments: Bool { true }
|
||||
var shouldShowHideAttachmentsButton: Bool { false }
|
||||
var sensitive: Bool { false }
|
||||
var canRemoveAttachments: Bool { false }
|
||||
func removeAttachment(viewModel: AttachmentViewModel) {}
|
||||
func toggleShowAttachments() {}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import Mastodon
|
||||
import ServiceLayer
|
||||
|
||||
public final class CompositionAttachmentViewModel: ObservableObject {
|
||||
public let attachment: Attachment
|
||||
|
||||
init(attachment: Attachment) {
|
||||
self.attachment = attachment
|
||||
}
|
||||
}
|
|
@ -5,18 +5,19 @@ import Foundation
|
|||
import Mastodon
|
||||
import ServiceLayer
|
||||
|
||||
public final class CompositionViewModel: ObservableObject, Identifiable {
|
||||
public final class CompositionViewModel: AttachmentsRenderingViewModel, ObservableObject, Identifiable {
|
||||
public let id = Id()
|
||||
public var isPosted = false
|
||||
@Published public var text = ""
|
||||
@Published public var contentWarning = ""
|
||||
@Published public var displayContentWarning = false
|
||||
@Published public private(set) var attachmentViewModels = [CompositionAttachmentViewModel]()
|
||||
@Published public private(set) var attachmentViewModels = [AttachmentViewModel]()
|
||||
@Published public private(set) var attachmentUpload: AttachmentUpload?
|
||||
@Published public private(set) var isPostable = false
|
||||
@Published public private(set) var canAddAttachment = true
|
||||
@Published public private(set) var canAddNonImageAttachment = true
|
||||
@Published public private(set) var remainingCharacters = CompositionViewModel.maxCharacters
|
||||
public let canRemoveAttachments = true
|
||||
|
||||
private var attachmentUploadCancellable: AnyCancellable?
|
||||
|
||||
|
@ -42,6 +43,14 @@ public final class CompositionViewModel: ObservableObject, Identifiable {
|
|||
.map { Self.maxCharacters - ($0 + ($1 ? $2.count : 0)) }
|
||||
.assign(to: &$remainingCharacters)
|
||||
}
|
||||
|
||||
public func attachmentSelected(viewModel: AttachmentViewModel) {
|
||||
|
||||
}
|
||||
|
||||
public func removeAttachment(viewModel: AttachmentViewModel) {
|
||||
attachmentViewModels.removeAll { $0 === viewModel }
|
||||
}
|
||||
}
|
||||
|
||||
public extension CompositionViewModel {
|
||||
|
@ -64,10 +73,6 @@ public extension CompositionViewModel {
|
|||
visibility: visibility)
|
||||
}
|
||||
|
||||
func remove(attachmentViewModel: CompositionAttachmentViewModel) {
|
||||
attachmentViewModels.removeAll { $0 === attachmentViewModel }
|
||||
}
|
||||
|
||||
func cancelUpload() {
|
||||
attachmentUploadCancellable?.cancel()
|
||||
}
|
||||
|
@ -96,7 +101,10 @@ extension CompositionViewModel {
|
|||
.sink { [weak self] _ in
|
||||
self?.attachmentUpload = nil
|
||||
} receiveValue: { [weak self] in
|
||||
self?.attachmentViewModels.append(CompositionAttachmentViewModel(attachment: $0))
|
||||
self?.attachmentViewModels.append(
|
||||
AttachmentViewModel(
|
||||
attachment: $0,
|
||||
identification: parentViewModel.identification))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import Foundation
|
|||
import Mastodon
|
||||
import ServiceLayer
|
||||
|
||||
public final class StatusViewModel: CollectionItemViewModel, ObservableObject {
|
||||
public final class StatusViewModel: CollectionItemViewModel, AttachmentsRenderingViewModel, ObservableObject {
|
||||
public let content: NSAttributedString
|
||||
public let contentEmoji: [Emoji]
|
||||
public let displayName: String
|
||||
|
@ -40,7 +40,7 @@ public final class StatusViewModel: CollectionItemViewModel, ObservableObject {
|
|||
: statusService.status.account.displayName
|
||||
rebloggedByDisplayNameEmoji = statusService.status.account.emojis
|
||||
attachmentViewModels = statusService.status.displayStatus.mediaAttachments
|
||||
.map { AttachmentViewModel(attachment: $0, status: statusService.status, identification: identification) }
|
||||
.map { AttachmentViewModel(attachment: $0, identification: identification, status: statusService.status) }
|
||||
pollEmoji = statusService.status.displayStatus.poll?.emojis ?? []
|
||||
events = eventsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@ import Kingfisher
|
|||
import UIKit
|
||||
import ViewModels
|
||||
|
||||
final class StatusAttachmentView: UIView {
|
||||
final class AttachmentView: UIView {
|
||||
let playerView = PlayerView()
|
||||
let imageView = AnimatedImageView()
|
||||
let button = UIButton()
|
||||
let removeButton = UIButton(type: .close)
|
||||
let selectionButton = UIButton()
|
||||
|
||||
var playing: Bool = false {
|
||||
didSet {
|
||||
|
@ -25,10 +26,12 @@ final class StatusAttachmentView: UIView {
|
|||
}
|
||||
|
||||
private let viewModel: AttachmentViewModel
|
||||
private let parentViewModel: AttachmentsRenderingViewModel
|
||||
private var playerLooper: AVPlayerLooper?
|
||||
|
||||
init(viewModel: AttachmentViewModel) {
|
||||
init(viewModel: AttachmentViewModel, parentViewModel: AttachmentsRenderingViewModel) {
|
||||
self.viewModel = viewModel
|
||||
self.parentViewModel = parentViewModel
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
|
@ -72,7 +75,7 @@ final class StatusAttachmentView: UIView {
|
|||
}
|
||||
}
|
||||
|
||||
extension StatusAttachmentView {
|
||||
extension AttachmentView {
|
||||
func play() {
|
||||
let player = PlayerCache.shared.player(url: viewModel.attachment.url)
|
||||
|
||||
|
@ -105,7 +108,7 @@ extension StatusAttachmentView {
|
|||
}
|
||||
}
|
||||
|
||||
private extension StatusAttachmentView {
|
||||
private extension AttachmentView {
|
||||
static var playerLooperCache = [AVQueuePlayer: AVPlayerLooper]()
|
||||
|
||||
// swiftlint:disable:next function_body_length
|
||||
|
@ -139,9 +142,30 @@ private extension StatusAttachmentView {
|
|||
playerView.videoGravity = .resizeAspectFill
|
||||
playerView.isHidden = true
|
||||
|
||||
addSubview(button)
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setBackgroundImage(.highlightedButtonBackground, for: .highlighted)
|
||||
addSubview(selectionButton)
|
||||
selectionButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
selectionButton.setBackgroundImage(.highlightedButtonBackground, for: .highlighted)
|
||||
selectionButton.addAction(
|
||||
UIAction { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.parentViewModel.attachmentSelected(viewModel: self.viewModel)
|
||||
},
|
||||
for: .touchUpInside)
|
||||
|
||||
addSubview(removeButton)
|
||||
removeButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
removeButton.showsMenuAsPrimaryAction = true
|
||||
removeButton.menu = UIMenu(
|
||||
children: [
|
||||
UIAction(
|
||||
title: NSLocalizedString("remove", comment: ""),
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.parentViewModel.removeAttachment(viewModel: self.viewModel)
|
||||
}])
|
||||
|
||||
switch viewModel.attachment.type {
|
||||
case .image, .video, .gifv:
|
||||
|
@ -183,10 +207,12 @@ private extension StatusAttachmentView {
|
|||
equalTo: playView.topAnchor, constant: .compactSpacing),
|
||||
playImageView.leadingAnchor.constraint(
|
||||
equalTo: playView.leadingAnchor, constant: .compactSpacing),
|
||||
button.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
button.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
button.topAnchor.constraint(equalTo: topAnchor),
|
||||
button.bottomAnchor.constraint(equalTo: bottomAnchor)
|
||||
selectionButton.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
selectionButton.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
selectionButton.topAnchor.constraint(equalTo: topAnchor),
|
||||
selectionButton.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
removeButton.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
|
||||
removeButton.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor)
|
||||
])
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import Combine
|
|||
import UIKit
|
||||
import ViewModels
|
||||
|
||||
final class StatusAttachmentsView: UIView {
|
||||
final class AttachmentsView: UIView {
|
||||
private let containerStackView = UIStackView()
|
||||
private let leftStackView = UIStackView()
|
||||
private let rightStackView = UIStackView()
|
||||
|
@ -15,7 +15,7 @@ final class StatusAttachmentsView: UIView {
|
|||
private var aspectRatioConstraint: NSLayoutConstraint?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var viewModel: StatusViewModel? {
|
||||
var viewModel: AttachmentsRenderingViewModel? {
|
||||
didSet {
|
||||
for stackView in [leftStackView, rightStackView] {
|
||||
for view in stackView.arrangedSubviews {
|
||||
|
@ -24,21 +24,18 @@ final class StatusAttachmentsView: UIView {
|
|||
}
|
||||
}
|
||||
|
||||
let attachmentViewModels = viewModel?.attachmentViewModels ?? []
|
||||
let attachmentCount = attachmentViewModels.count
|
||||
guard let viewModel = viewModel else { return }
|
||||
|
||||
rightStackView.isHidden = attachmentCount == 1
|
||||
rightStackView.isHidden = viewModel.attachmentViewModels.count == 1
|
||||
|
||||
for (index, attachmentViewModel) in attachmentViewModels.enumerated() {
|
||||
let attachmentView = StatusAttachmentView(viewModel: attachmentViewModel)
|
||||
for (index, attachmentViewModel) in viewModel.attachmentViewModels.enumerated() {
|
||||
let attachmentView = AttachmentView(viewModel: attachmentViewModel, parentViewModel: viewModel)
|
||||
attachmentView.playing = viewModel.shouldShowAttachments && attachmentViewModel.shouldAutoplay
|
||||
attachmentView.removeButton.isHidden = !viewModel.canRemoveAttachments
|
||||
|
||||
attachmentView.button.addAction(
|
||||
UIAction { [weak self] _ in self?.viewModel?.attachmentSelected(viewModel: attachmentViewModel) },
|
||||
for: .touchUpInside)
|
||||
|
||||
if attachmentCount == 2 && index == 1
|
||||
|| attachmentCount == 3 && index != 0
|
||||
|| attachmentCount > 3 && index % 2 != 0 {
|
||||
if viewModel.attachmentViewModels.count == 2 && index == 1
|
||||
|| viewModel.attachmentViewModels.count == 3 && index != 0
|
||||
|| viewModel.attachmentViewModels.count > 3 && index % 2 != 0 {
|
||||
rightStackView.addArrangedSubview(attachmentView)
|
||||
} else {
|
||||
leftStackView.addArrangedSubview(attachmentView)
|
||||
|
@ -47,7 +44,8 @@ final class StatusAttachmentsView: UIView {
|
|||
|
||||
let newAspectRatio: CGFloat
|
||||
|
||||
if attachmentCount == 1, let aspectRatio = attachmentViewModels.first?.attachment.aspectRatio {
|
||||
if viewModel.attachmentViewModels.count == 1,
|
||||
let aspectRatio = viewModel.attachmentViewModels.first?.attachment.aspectRatio {
|
||||
newAspectRatio = max(CGFloat(aspectRatio), 16 / 9)
|
||||
} else {
|
||||
newAspectRatio = 16 / 9
|
||||
|
@ -58,14 +56,14 @@ final class StatusAttachmentsView: UIView {
|
|||
aspectRatioConstraint?.priority = .justBelowMax
|
||||
aspectRatioConstraint?.isActive = true
|
||||
|
||||
curtain.isHidden = viewModel?.shouldShowAttachments ?? false
|
||||
curtain.isHidden = viewModel.shouldShowAttachments
|
||||
curtainButton.setTitle(
|
||||
NSLocalizedString((viewModel?.sensitive ?? false)
|
||||
NSLocalizedString((viewModel.sensitive)
|
||||
? "attachment.sensitive-content"
|
||||
: "attachment.media-hidden",
|
||||
comment: ""),
|
||||
for: .normal)
|
||||
hideButtonBackground.isHidden = !(viewModel?.shouldShowHideAttachmentsButton ?? false)
|
||||
hideButtonBackground.isHidden = !viewModel.shouldShowHideAttachmentsButton
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +79,7 @@ final class StatusAttachmentsView: UIView {
|
|||
}
|
||||
}
|
||||
|
||||
extension StatusAttachmentsView {
|
||||
extension AttachmentsView {
|
||||
var shouldAutoplay: Bool {
|
||||
guard !isHidden, let viewModel = viewModel, viewModel.shouldShowAttachments else { return false }
|
||||
|
||||
|
@ -89,7 +87,7 @@ extension StatusAttachmentsView {
|
|||
}
|
||||
}
|
||||
|
||||
private extension StatusAttachmentsView {
|
||||
private extension AttachmentsView {
|
||||
// swiftlint:disable:next function_body_length
|
||||
func initialSetup() {
|
||||
backgroundColor = .clear
|
||||
|
@ -161,20 +159,10 @@ private extension StatusAttachmentsView {
|
|||
curtainButton.trailingAnchor.constraint(equalTo: curtain.contentView.trailingAnchor),
|
||||
curtainButton.bottomAnchor.constraint(equalTo: curtain.contentView.bottomAnchor)
|
||||
])
|
||||
|
||||
NotificationCenter.default.publisher(for: TableViewController.autoplayableAttachmentsViewNotification)
|
||||
.sink { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
for attachmentView in self.attachmentViews {
|
||||
attachmentView.playing = $0.object as? Self === self
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
var attachmentViews: [StatusAttachmentView] {
|
||||
var attachmentViews: [AttachmentView] {
|
||||
(leftStackView.arrangedSubviews + rightStackView.arrangedSubviews)
|
||||
.compactMap { $0 as? StatusAttachmentView }
|
||||
.compactMap { $0 as? AttachmentView }
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import ViewModels
|
||||
|
||||
class CompositionAttachmentCollectionViewCell: UICollectionViewCell {
|
||||
var viewModel: CompositionAttachmentViewModel?
|
||||
var parentViewModel: CompositionViewModel?
|
||||
|
||||
override func updateConfiguration(using state: UICellConfigurationState) {
|
||||
guard let viewModel = viewModel, let parentViewModel = parentViewModel else { return }
|
||||
|
||||
contentConfiguration = CompositionAttachmentContentConfiguration(
|
||||
viewModel: viewModel,
|
||||
parentViewModel: parentViewModel)
|
||||
.updated(for: state)
|
||||
backgroundConfiguration = UIBackgroundConfiguration.clear().updated(for: state)
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import ViewModels
|
||||
|
||||
struct CompositionAttachmentContentConfiguration {
|
||||
let viewModel: CompositionAttachmentViewModel
|
||||
let parentViewModel: CompositionViewModel
|
||||
}
|
||||
|
||||
extension CompositionAttachmentContentConfiguration: UIContentConfiguration {
|
||||
func makeContentView() -> UIView & UIContentView {
|
||||
CompositionAttachmentView(configuration: self)
|
||||
}
|
||||
|
||||
func updated(for state: UIConfigurationState) -> CompositionAttachmentContentConfiguration {
|
||||
self
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
// Copyright © 2020 Metabolist. All rights reserved.
|
||||
|
||||
import Kingfisher
|
||||
import UIKit
|
||||
import ViewModels
|
||||
|
||||
class CompositionAttachmentView: UIView {
|
||||
let imageView = UIImageView()
|
||||
let removeButton = UIButton()
|
||||
let editButton = UIButton()
|
||||
private var compositionAttachmentConfiguration: CompositionAttachmentContentConfiguration
|
||||
private var aspectRatioConstraint: NSLayoutConstraint
|
||||
|
||||
init(configuration: CompositionAttachmentContentConfiguration) {
|
||||
self.compositionAttachmentConfiguration = configuration
|
||||
|
||||
aspectRatioConstraint = imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor, multiplier: 2)
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
initialSetup()
|
||||
applyCompositionAttachmentConfiguration()
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
extension CompositionAttachmentView: UIContentView {
|
||||
var configuration: UIContentConfiguration {
|
||||
get { compositionAttachmentConfiguration }
|
||||
set {
|
||||
guard let compositionAttachmentConfiguration = newValue as? CompositionAttachmentContentConfiguration
|
||||
else { return }
|
||||
|
||||
self.compositionAttachmentConfiguration = compositionAttachmentConfiguration
|
||||
|
||||
applyCompositionAttachmentConfiguration()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension CompositionAttachmentView {
|
||||
// swiftlint:disable:next function_body_length
|
||||
func initialSetup() {
|
||||
backgroundColor = .secondarySystemBackground
|
||||
layer.cornerRadius = .defaultCornerRadius
|
||||
clipsToBounds = true
|
||||
|
||||
addSubview(imageView)
|
||||
imageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.kf.indicatorType = .activity
|
||||
|
||||
addSubview(removeButton)
|
||||
removeButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
removeButton.setImage(
|
||||
UIImage(
|
||||
systemName: "xmark.circle.fill",
|
||||
withConfiguration: UIImage.SymbolConfiguration(scale: .large)),
|
||||
for: .normal)
|
||||
removeButton.showsMenuAsPrimaryAction = true
|
||||
removeButton.menu = UIMenu(
|
||||
children: [
|
||||
UIAction(
|
||||
title: NSLocalizedString("remove", comment: ""),
|
||||
image: UIImage(systemName: "trash"),
|
||||
attributes: .destructive, handler: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.compositionAttachmentConfiguration.parentViewModel.remove(
|
||||
attachmentViewModel: self.compositionAttachmentConfiguration.viewModel)
|
||||
})])
|
||||
|
||||
addSubview(editButton)
|
||||
editButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
editButton.setImage(
|
||||
UIImage(
|
||||
systemName: "pencil.circle.fill",
|
||||
withConfiguration: UIImage.SymbolConfiguration(scale: .large)),
|
||||
for: .normal)
|
||||
editButton.addAction(UIAction { [weak self] _ in }, for: .touchUpInside)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
aspectRatioConstraint,
|
||||
imageView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
imageView.topAnchor.constraint(equalTo: topAnchor),
|
||||
imageView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
imageView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
removeButton.topAnchor.constraint(equalTo: topAnchor),
|
||||
removeButton.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
removeButton.heightAnchor.constraint(equalToConstant: .minimumButtonDimension),
|
||||
removeButton.widthAnchor.constraint(equalToConstant: .minimumButtonDimension),
|
||||
editButton.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
editButton.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
editButton.heightAnchor.constraint(equalToConstant: .minimumButtonDimension),
|
||||
editButton.widthAnchor.constraint(equalToConstant: .minimumButtonDimension)
|
||||
])
|
||||
}
|
||||
|
||||
func applyCompositionAttachmentConfiguration() {
|
||||
imageView.kf.setImage(with: compositionAttachmentConfiguration.viewModel.attachment.previewUrl)
|
||||
aspectRatioConstraint.isActive = false
|
||||
aspectRatioConstraint = imageView.widthAnchor.constraint(
|
||||
equalTo: imageView.heightAnchor,
|
||||
multiplier: CGFloat(compositionAttachmentConfiguration.viewModel.attachment.aspectRatio ?? 1))
|
||||
aspectRatioConstraint.priority = .justBelowMax
|
||||
aspectRatioConstraint.isActive = true
|
||||
}
|
||||
}
|
|
@ -10,46 +10,17 @@ final class CompositionView: UIView {
|
|||
let spoilerTextField = UITextField()
|
||||
let textView = UITextView()
|
||||
let textViewPlaceholder = UILabel()
|
||||
let attachmentsCollectionView: UICollectionView
|
||||
let attachmentsView = AttachmentsView()
|
||||
let attachmentUploadView: AttachmentUploadView
|
||||
|
||||
private let viewModel: CompositionViewModel
|
||||
private let parentViewModel: NewStatusViewModel
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private lazy var attachmentsDataSource: CompositionAttachmentsDataSource = {
|
||||
let vm = viewModel
|
||||
|
||||
return .init(collectionView: attachmentsCollectionView) {
|
||||
(vm.attachmentViewModels[$0.item], vm)
|
||||
}
|
||||
}()
|
||||
|
||||
init(viewModel: CompositionViewModel, parentViewModel: NewStatusViewModel) {
|
||||
self.viewModel = viewModel
|
||||
self.parentViewModel = parentViewModel
|
||||
|
||||
let itemSize = NSCollectionLayoutSize(
|
||||
widthDimension: .estimated(Self.attachmentCollectionViewHeight),
|
||||
heightDimension: .fractionalHeight(1))
|
||||
let item = NSCollectionLayoutItem(layoutSize: itemSize)
|
||||
let groupSize = NSCollectionLayoutSize(
|
||||
widthDimension: .estimated(Self.attachmentCollectionViewHeight),
|
||||
heightDimension: .fractionalHeight(1))
|
||||
let group = NSCollectionLayoutGroup.horizontal(
|
||||
layoutSize: groupSize,
|
||||
subitems: [item])
|
||||
let section = NSCollectionLayoutSection(group: group)
|
||||
|
||||
section.interGroupSpacing = .defaultSpacing
|
||||
|
||||
let configuration = UICollectionViewCompositionalLayoutConfiguration()
|
||||
|
||||
configuration.scrollDirection = .horizontal
|
||||
|
||||
let attachmentsLayout = UICollectionViewCompositionalLayout(section: section, configuration: configuration)
|
||||
|
||||
attachmentsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: attachmentsLayout)
|
||||
attachmentUploadView = AttachmentUploadView(viewModel: viewModel)
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
@ -126,10 +97,7 @@ private extension CompositionView {
|
|||
textViewPlaceholder.textColor = .secondaryLabel
|
||||
textViewPlaceholder.text = NSLocalizedString("compose.prompt", comment: "")
|
||||
|
||||
stackView.addArrangedSubview(attachmentsCollectionView)
|
||||
attachmentsCollectionView.dataSource = attachmentsDataSource
|
||||
attachmentsCollectionView.backgroundColor = .clear
|
||||
|
||||
stackView.addArrangedSubview(attachmentsView)
|
||||
stackView.addArrangedSubview(attachmentUploadView)
|
||||
|
||||
textView.text = viewModel.text
|
||||
|
@ -163,9 +131,10 @@ private extension CompositionView {
|
|||
.store(in: &cancellables)
|
||||
|
||||
viewModel.$attachmentViewModels
|
||||
.receive(on: RunLoop.main)
|
||||
.sink { [weak self] in
|
||||
self?.attachmentsDataSource.apply($0.map(\.attachment).snapshot())
|
||||
self?.attachmentsCollectionView.isHidden = $0.isEmpty
|
||||
self?.attachmentsView.viewModel = self?.viewModel
|
||||
self?.attachmentsView.isHidden = $0.isEmpty
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
|
@ -182,8 +151,7 @@ private extension CompositionView {
|
|||
stackView.bottomAnchor.constraint(lessThanOrEqualTo: guide.bottomAnchor),
|
||||
textViewPlaceholder.leadingAnchor.constraint(equalTo: textView.leadingAnchor),
|
||||
textViewPlaceholder.topAnchor.constraint(equalTo: textView.topAnchor),
|
||||
textViewPlaceholder.trailingAnchor.constraint(equalTo: textView.trailingAnchor),
|
||||
attachmentsCollectionView.heightAnchor.constraint(equalToConstant: Self.attachmentCollectionViewHeight)
|
||||
textViewPlaceholder.trailingAnchor.constraint(equalTo: textView.trailingAnchor)
|
||||
]
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
|
|
|
@ -7,7 +7,7 @@ final class StatusBodyView: UIView {
|
|||
let spoilerTextLabel = UILabel()
|
||||
let toggleShowContentButton = UIButton(type: .system)
|
||||
let contentTextView = TouchFallthroughTextView()
|
||||
let attachmentsView = StatusAttachmentsView()
|
||||
let attachmentsView = AttachmentsView()
|
||||
let pollView = PollView()
|
||||
let cardView = CardView()
|
||||
|
||||
|
|
Loading…
Reference in a new issue