mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-25 01:31:02 +00:00
wip
This commit is contained in:
parent
d7c73ee06d
commit
0c5a3de66b
15 changed files with 223 additions and 62 deletions
|
@ -19,20 +19,6 @@ public class Composition {
|
||||||
|
|
||||||
public extension Composition {
|
public extension Composition {
|
||||||
typealias Id = UUID
|
typealias Id = UUID
|
||||||
|
|
||||||
struct Attachment {
|
|
||||||
public let data: Data
|
|
||||||
public let type: Mastodon.Attachment.AttachmentType
|
|
||||||
public let mimeType: String
|
|
||||||
public var description: String?
|
|
||||||
public var focus: Mastodon.Attachment.Meta.Focus?
|
|
||||||
|
|
||||||
public init(data: Data, type: Mastodon.Attachment.AttachmentType, mimeType: String) {
|
|
||||||
self.data = data
|
|
||||||
self.type = type
|
|
||||||
self.mimeType = mimeType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Composition {
|
extension Composition {
|
||||||
|
|
13
Data Sources/CompositionAttachmentsDataSource.swift
Normal file
13
Data Sources/CompositionAttachmentsDataSource.swift
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Mastodon
|
||||||
|
import UIKit
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
final class CompositionAttachmentsDataSource: UICollectionViewDiffableDataSource<Int, Attachment> {
|
||||||
|
// init(collectionView: UICollectionView, composition: Composition) {
|
||||||
|
// super.init(collectionView: collectionView) { collectionView, indexPath, attachment in
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
|
@ -19,14 +19,14 @@ open class HTTPClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
open func dataTaskPublisher<T: DecodableTarget>(
|
open func dataTaskPublisher<T: DecodableTarget>(
|
||||||
_ target: T) -> AnyPublisher<(data: Data, response: HTTPURLResponse), Error> {
|
_ target: T, progress: Progress? = nil) -> AnyPublisher<(data: Data, response: HTTPURLResponse), Error> {
|
||||||
if let protocolClasses = session.configuration.protocolClasses {
|
if let protocolClasses = session.configuration.protocolClasses {
|
||||||
for protocolClass in protocolClasses {
|
for protocolClass in protocolClasses {
|
||||||
(protocolClass as? TargetProcessing.Type)?.process(target: target)
|
(protocolClass as? TargetProcessing.Type)?.process(target: target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return session.dataTaskPublisher(for: target.urlRequest())
|
return session.dataTaskPublisher(for: target.urlRequest(), progress: progress)
|
||||||
.tryMap { data, response in
|
.tryMap { data, response in
|
||||||
guard let httpResponse = response as? HTTPURLResponse else {
|
guard let httpResponse = response as? HTTPURLResponse else {
|
||||||
throw HTTPError.nonHTTPURLResponse(data: data, response: response)
|
throw HTTPError.nonHTTPURLResponse(data: data, response: response)
|
||||||
|
@ -41,8 +41,8 @@ open class HTTPClient {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
open func request<T: DecodableTarget>(_ target: T) -> AnyPublisher<T.ResultType, Error> {
|
open func request<T: DecodableTarget>(_ target: T, progress: Progress? = nil) -> AnyPublisher<T.ResultType, Error> {
|
||||||
dataTaskPublisher(target)
|
dataTaskPublisher(target, progress: progress)
|
||||||
.map(\.data)
|
.map(\.data)
|
||||||
.decode(type: T.ResultType.self, decoder: decoder)
|
.decode(type: T.ResultType.self, decoder: decoder)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
|
29
HTTP/Sources/HTTP/URLSession+Extensions.swift
Normal file
29
HTTP/Sources/HTTP/URLSession+Extensions.swift
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension URLSession {
|
||||||
|
func dataTaskPublisher(for request: URLRequest, progress: Progress?)
|
||||||
|
-> AnyPublisher<DataTaskPublisher.Output, Error> {
|
||||||
|
if let progress = progress {
|
||||||
|
return Deferred {
|
||||||
|
Future<DataTaskPublisher.Output, Error> { promise in
|
||||||
|
let dataTask = self.dataTask(with: request) { data, response, error in
|
||||||
|
if let error = error {
|
||||||
|
promise(.failure(error))
|
||||||
|
} else if let data = data, let response = response {
|
||||||
|
promise(.success((data, response)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.addChild(dataTask.progress, withPendingUnitCount: 1)
|
||||||
|
dataTask.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
} else {
|
||||||
|
return dataTaskPublisher(for: request).mapError { $0 as Error }.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,8 @@ public final class MastodonAPIClient: HTTPClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func dataTaskPublisher<T: DecodableTarget>(
|
public override func dataTaskPublisher<T: DecodableTarget>(
|
||||||
_ target: T) -> AnyPublisher<(data: Data, response: HTTPURLResponse), Error> {
|
_ target: T, progress: Progress? = nil) -> AnyPublisher<(data: Data, response: HTTPURLResponse), Error> {
|
||||||
super.dataTaskPublisher(target)
|
super.dataTaskPublisher(target, progress: progress)
|
||||||
.mapError { [weak self] error -> Error in
|
.mapError { [weak self] error -> Error in
|
||||||
if case let HTTPError.invalidStatusCode(data, _) = error,
|
if case let HTTPError.invalidStatusCode(data, _) = error,
|
||||||
let apiError = try? self?.decoder.decode(APIError.self, from: data) {
|
let apiError = try? self?.decoder.decode(APIError.self, from: data) {
|
||||||
|
@ -30,8 +30,8 @@ public final class MastodonAPIClient: HTTPClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MastodonAPIClient {
|
extension MastodonAPIClient {
|
||||||
public func request<E: Endpoint>(_ endpoint: E) -> AnyPublisher<E.ResultType, Error> {
|
public func request<E: Endpoint>(_ endpoint: E, progress: Progress? = nil) -> AnyPublisher<E.ResultType, Error> {
|
||||||
dataTaskPublisher(target(endpoint: endpoint))
|
dataTaskPublisher(target(endpoint: endpoint), progress: progress)
|
||||||
.map(\.data)
|
.map(\.data)
|
||||||
.decode(type: E.ResultType.self, decoder: decoder)
|
.decode(type: E.ResultType.self, decoder: decoder)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
|
@ -42,9 +42,10 @@ extension MastodonAPIClient {
|
||||||
maxId: String? = nil,
|
maxId: String? = nil,
|
||||||
minId: String? = nil,
|
minId: String? = nil,
|
||||||
sinceId: String? = nil,
|
sinceId: String? = nil,
|
||||||
limit: Int? = nil) -> AnyPublisher<PagedResult<E.ResultType>, Error> {
|
limit: Int? = nil,
|
||||||
|
progress: Progress? = nil) -> AnyPublisher<PagedResult<E.ResultType>, Error> {
|
||||||
let pagedTarget = target(endpoint: Paged(endpoint, maxId: maxId, minId: minId, sinceId: sinceId, limit: limit))
|
let pagedTarget = target(endpoint: Paged(endpoint, maxId: maxId, minId: minId, sinceId: sinceId, limit: limit))
|
||||||
let dataTask = dataTaskPublisher(pagedTarget).share()
|
let dataTask = dataTaskPublisher(pagedTarget, progress: progress).share()
|
||||||
let decoded = dataTask.map(\.data).decode(type: E.ResultType.self, decoder: decoder)
|
let decoded = dataTask.map(\.data).decode(type: E.ResultType.self, decoder: decoder)
|
||||||
let info = dataTask.map { _, response -> PagedResult<E.ResultType>.Info in
|
let info = dataTask.map { _, response -> PagedResult<E.ResultType>.Info in
|
||||||
var maxId: String?
|
var maxId: String?
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04226FC2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift */; };
|
D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D04226FC2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift */; };
|
||||||
D0625E59250F092900502611 /* StatusListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E58250F092900502611 /* StatusListCell.swift */; };
|
D0625E59250F092900502611 /* StatusListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E58250F092900502611 /* StatusListCell.swift */; };
|
||||||
D0625E5D250F0B5C00502611 /* StatusContentConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0625E5C250F0B5C00502611 /* StatusContentConfiguration.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 */; };
|
||||||
D06B492324D4611300642749 /* KingfisherSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = D06B492224D4611300642749 /* KingfisherSwiftUI */; };
|
D06B492324D4611300642749 /* KingfisherSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = D06B492224D4611300642749 /* KingfisherSwiftUI */; };
|
||||||
D06BC5E625202AD90079541D /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06BC5E525202AD90079541D /* ProfileViewController.swift */; };
|
D06BC5E625202AD90079541D /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06BC5E525202AD90079541D /* ProfileViewController.swift */; };
|
||||||
D08B8D3D253F929E00B1EBEF /* ImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D3C253F929E00B1EBEF /* ImageViewController.swift */; };
|
D08B8D3D253F929E00B1EBEF /* ImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08B8D3C253F929E00B1EBEF /* ImageViewController.swift */; };
|
||||||
|
@ -91,6 +95,8 @@
|
||||||
D0C7D4D724F7616A001EBDBB /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */; };
|
D0C7D4D724F7616A001EBDBB /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */; };
|
||||||
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */; };
|
D0C7D4D924F7616A001EBDBB /* KingfisherOptionsInfo+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */; };
|
||||||
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46F24F76169001EBDBB /* View+Extensions.swift */; };
|
D0C7D4DA24F7616A001EBDBB /* View+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C7D46F24F76169001EBDBB /* View+Extensions.swift */; };
|
||||||
|
D0CE9F87258B076900E3A6B6 /* AttachmentUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CE9F86258B076900E3A6B6 /* AttachmentUploadView.swift */; };
|
||||||
|
D0CE9F88258B076900E3A6B6 /* AttachmentUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CE9F86258B076900E3A6B6 /* AttachmentUploadView.swift */; };
|
||||||
D0DD50CB256B1F24004A04F7 /* ReportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DD50CA256B1F24004A04F7 /* ReportView.swift */; };
|
D0DD50CB256B1F24004A04F7 /* ReportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DD50CA256B1F24004A04F7 /* ReportView.swift */; };
|
||||||
D0E1F583251F13EC00D45315 /* WebfingerIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E1F582251F13EC00D45315 /* WebfingerIndicatorView.swift */; };
|
D0E1F583251F13EC00D45315 /* WebfingerIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E1F582251F13EC00D45315 /* WebfingerIndicatorView.swift */; };
|
||||||
D0E2C1D124FD97F000854680 /* ViewModels in Frameworks */ = {isa = PBXBuildFile; productRef = D0E2C1D024FD97F000854680 /* ViewModels */; };
|
D0E2C1D124FD97F000854680 /* ViewModels in Frameworks */ = {isa = PBXBuildFile; productRef = D0E2C1D024FD97F000854680 /* ViewModels */; };
|
||||||
|
@ -180,6 +186,8 @@
|
||||||
D047FA8C24C3E21200AF17C5 /* Metatext.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metatext.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
D06BC5E525202AD90079541D /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -245,6 +253,7 @@
|
||||||
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
|
D0C7D46C24F76169001EBDBB /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KingfisherOptionsInfo+Extensions.swift"; sourceTree = "<group>"; };
|
D0C7D46E24F76169001EBDBB /* KingfisherOptionsInfo+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KingfisherOptionsInfo+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = "<group>"; };
|
D0C7D46F24F76169001EBDBB /* View+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
D0CE9F86258B076900E3A6B6 /* AttachmentUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentUploadView.swift; sourceTree = "<group>"; };
|
||||||
D0D7C013250440610039AD6F /* CodableBloomFilter */ = {isa = PBXFileReference; lastKnownFileType = folder; path = CodableBloomFilter; sourceTree = "<group>"; };
|
D0D7C013250440610039AD6F /* CodableBloomFilter */ = {isa = PBXFileReference; lastKnownFileType = folder; path = CodableBloomFilter; sourceTree = "<group>"; };
|
||||||
D0DD50CA256B1F24004A04F7 /* ReportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportView.swift; sourceTree = "<group>"; };
|
D0DD50CA256B1F24004A04F7 /* ReportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportView.swift; sourceTree = "<group>"; };
|
||||||
D0E0F1E424FC49FC002C04BF /* Mastodon */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Mastodon; sourceTree = "<group>"; };
|
D0E0F1E424FC49FC002C04BF /* Mastodon */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Mastodon; sourceTree = "<group>"; };
|
||||||
|
@ -410,6 +419,7 @@
|
||||||
children = (
|
children = (
|
||||||
D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */,
|
D0A1F4F6252E7D4B004435BF /* TableViewDataSource.swift */,
|
||||||
D0F2D4D0257EE84400986197 /* NewStatusDataSource.swift */,
|
D0F2D4D0257EE84400986197 /* NewStatusDataSource.swift */,
|
||||||
|
D065965A25899DAE0096AC5D /* CompositionAttachmentsDataSource.swift */,
|
||||||
);
|
);
|
||||||
path = "Data Sources";
|
path = "Data Sources";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -432,6 +442,8 @@
|
||||||
D0F0B125251A90F400942152 /* AccountListCell.swift */,
|
D0F0B125251A90F400942152 /* AccountListCell.swift */,
|
||||||
D0F0B10D251A868200942152 /* AccountView.swift */,
|
D0F0B10D251A868200942152 /* AccountView.swift */,
|
||||||
D0C7D42424F76169001EBDBB /* AddIdentityView.swift */,
|
D0C7D42424F76169001EBDBB /* AddIdentityView.swift */,
|
||||||
|
D0CE9F86258B076900E3A6B6 /* AttachmentUploadView.swift */,
|
||||||
|
D065966025899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift */,
|
||||||
D08E52E2257D747400FA2C5F /* CompositionContentConfiguration.swift */,
|
D08E52E2257D747400FA2C5F /* CompositionContentConfiguration.swift */,
|
||||||
D0E9F9A9258450B300EF503D /* CompositionInputAccessoryView.swift */,
|
D0E9F9A9258450B300EF503D /* CompositionInputAccessoryView.swift */,
|
||||||
D08E52DB257D742B00FA2C5F /* CompositionListCell.swift */,
|
D08E52DB257D742B00FA2C5F /* CompositionListCell.swift */,
|
||||||
|
@ -772,6 +784,7 @@
|
||||||
D0F0B12E251A97E400942152 /* TableViewController.swift in Sources */,
|
D0F0B12E251A97E400942152 /* TableViewController.swift in Sources */,
|
||||||
D0DD50CB256B1F24004A04F7 /* ReportView.swift in Sources */,
|
D0DD50CB256B1F24004A04F7 /* ReportView.swift in Sources */,
|
||||||
D0FE1C8F253686F9003EF1EB /* PlayerView.swift in Sources */,
|
D0FE1C8F253686F9003EF1EB /* PlayerView.swift in Sources */,
|
||||||
|
D0CE9F87258B076900E3A6B6 /* AttachmentUploadView.swift in Sources */,
|
||||||
D0F0B113251A86A000942152 /* AccountContentConfiguration.swift in Sources */,
|
D0F0B113251A86A000942152 /* AccountContentConfiguration.swift in Sources */,
|
||||||
D036AA02254B6101009094DF /* NotificationListCell.swift in Sources */,
|
D036AA02254B6101009094DF /* NotificationListCell.swift in Sources */,
|
||||||
D08B8D42253F92B600B1EBEF /* ImagePageViewController.swift in Sources */,
|
D08B8D42253F92B600B1EBEF /* ImagePageViewController.swift in Sources */,
|
||||||
|
@ -806,6 +819,7 @@
|
||||||
D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */,
|
D036AA17254CA824009094DF /* StatusBodyView.swift in Sources */,
|
||||||
D08E512125786A6600FA2C5F /* UIButton+Extensions.swift in Sources */,
|
D08E512125786A6600FA2C5F /* UIButton+Extensions.swift in Sources */,
|
||||||
D0EA59482522B8B600804347 /* ViewConstants.swift in Sources */,
|
D0EA59482522B8B600804347 /* ViewConstants.swift in Sources */,
|
||||||
|
D065965B25899DAE0096AC5D /* CompositionAttachmentsDataSource.swift in Sources */,
|
||||||
D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */,
|
D04226FD2546AC0B000980A3 /* StartupAndSyncingPreferencesView.swift in Sources */,
|
||||||
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */,
|
D036AA0C254B612B009094DF /* NotificationContentConfiguration.swift in Sources */,
|
||||||
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
D0C7D49824F7616A001EBDBB /* CustomEmojiText.swift in Sources */,
|
||||||
|
@ -814,6 +828,7 @@
|
||||||
D08E52612579D2E100FA2C5F /* DomainBlocksView.swift in Sources */,
|
D08E52612579D2E100FA2C5F /* DomainBlocksView.swift in Sources */,
|
||||||
D01F41E424F8889700D55A2D /* StatusAttachmentsView.swift in Sources */,
|
D01F41E424F8889700D55A2D /* StatusAttachmentsView.swift in Sources */,
|
||||||
D00702312555F4AE00F38136 /* ConversationView.swift in Sources */,
|
D00702312555F4AE00F38136 /* ConversationView.swift in Sources */,
|
||||||
|
D065966125899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift in Sources */,
|
||||||
D0BEB21124FA2A91001B0F04 /* EditFilterView.swift in Sources */,
|
D0BEB21124FA2A91001B0F04 /* EditFilterView.swift in Sources */,
|
||||||
D08B8D4A253FC36500B1EBEF /* ImageNavigationController.swift in Sources */,
|
D08B8D4A253FC36500B1EBEF /* ImageNavigationController.swift in Sources */,
|
||||||
D0070252255921B100F38136 /* AccountFieldView.swift in Sources */,
|
D0070252255921B100F38136 /* AccountFieldView.swift in Sources */,
|
||||||
|
@ -851,12 +866,15 @@
|
||||||
D08E52DD257D742B00FA2C5F /* CompositionListCell.swift in Sources */,
|
D08E52DD257D742B00FA2C5F /* CompositionListCell.swift in Sources */,
|
||||||
D0E7AD4225870C79005F5E2D /* UIVIewController+Extensions.swift in Sources */,
|
D0E7AD4225870C79005F5E2D /* UIVIewController+Extensions.swift in Sources */,
|
||||||
D08E52EF257D757100FA2C5F /* CompositionView.swift in Sources */,
|
D08E52EF257D757100FA2C5F /* CompositionView.swift in Sources */,
|
||||||
|
D0CE9F88258B076900E3A6B6 /* AttachmentUploadView.swift in Sources */,
|
||||||
D0F2D5452581ABAB00986197 /* KingfisherOptionsInfo+Extensions.swift in Sources */,
|
D0F2D5452581ABAB00986197 /* KingfisherOptionsInfo+Extensions.swift in Sources */,
|
||||||
D08E52C7257C7AEE00FA2C5F /* ShareErrorViewController.swift in Sources */,
|
D08E52C7257C7AEE00FA2C5F /* ShareErrorViewController.swift in Sources */,
|
||||||
D08E52F8257D78BE00FA2C5F /* ViewConstants.swift in Sources */,
|
D08E52F8257D78BE00FA2C5F /* ViewConstants.swift in Sources */,
|
||||||
D0F2D4D6257EED6100986197 /* NewStatusDataSource.swift in Sources */,
|
D0F2D4D6257EED6100986197 /* NewStatusDataSource.swift in Sources */,
|
||||||
|
D065966725899E910096AC5D /* CompositionAttachmentsDataSource.swift in Sources */,
|
||||||
D08E52FD257D78CB00FA2C5F /* UIColor+Extensions.swift in Sources */,
|
D08E52FD257D78CB00FA2C5F /* UIColor+Extensions.swift in Sources */,
|
||||||
D08E52E4257D747400FA2C5F /* CompositionContentConfiguration.swift in Sources */,
|
D08E52E4257D747400FA2C5F /* CompositionContentConfiguration.swift in Sources */,
|
||||||
|
D065966225899E890096AC5D /* CompositionAttachmentCollectionViewCell.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -206,17 +206,26 @@ public extension IdentityService {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func uploadAttachment(data: Data, mimeType: String, progress: Progress) -> AnyPublisher<Attachment, Error> {
|
||||||
|
mastodonAPIClient.request(
|
||||||
|
AttachmentEndpoint.create(data: data, mimeType: mimeType, description: nil, focus: nil),
|
||||||
|
progress: progress)
|
||||||
|
}
|
||||||
|
|
||||||
func post(compositions: [Composition]) -> AnyPublisher<Never, Error> {
|
func post(compositions: [Composition]) -> AnyPublisher<Never, Error> {
|
||||||
guard let composition = compositions.first else { fatalError() }
|
fatalError()
|
||||||
guard let attachment = composition.attachments.first else { fatalError() }
|
// guard let composition = compositions.first else { fatalError() }
|
||||||
return mastodonAPIClient.request(AttachmentEndpoint.create(
|
|
||||||
data: attachment.data,
|
// guard let attachment = composition.attachments.first else { fatalError() }
|
||||||
mimeType: attachment.mimeType,
|
// return mastodonAPIClient.request(AttachmentEndpoint.create(
|
||||||
description: attachment.description,
|
// data: attachment.data,
|
||||||
focus: attachment.focus))
|
// mimeType: attachment.mimeType,
|
||||||
.print()
|
// description: attachment.description,
|
||||||
.ignoreOutput()
|
// focus: attachment.focus))
|
||||||
.eraseToAnyPublisher()
|
// .print()
|
||||||
|
// .ignoreOutput()
|
||||||
|
// .eraseToAnyPublisher()
|
||||||
|
|
||||||
// var components = StatusEndpoint.Components()
|
// var components = StatusEndpoint.Components()
|
||||||
//
|
//
|
||||||
// if !composition.text.isEmpty {
|
// if !composition.text.isEmpty {
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import ImageIO
|
import ImageIO
|
||||||
import Mastodon
|
|
||||||
import UniformTypeIdentifiers
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
enum MediaProcessingError: Error {
|
enum MediaProcessingError: Error {
|
||||||
|
@ -18,32 +17,18 @@ enum MediaProcessingError: Error {
|
||||||
public struct MediaProcessingService {}
|
public struct MediaProcessingService {}
|
||||||
|
|
||||||
public extension MediaProcessingService {
|
public extension MediaProcessingService {
|
||||||
static func attachment(itemProvider: NSItemProvider) -> AnyPublisher<Composition.Attachment, Error> {
|
static func dataAndMimeType(itemProvider: NSItemProvider) -> AnyPublisher<(data: Data, mimeType: String), Error> {
|
||||||
let registeredTypes = itemProvider.registeredTypeIdentifiers.compactMap(UTType.init)
|
let registeredTypes = itemProvider.registeredTypeIdentifiers.compactMap(UTType.init)
|
||||||
|
|
||||||
guard let uniformType = registeredTypes.first(where: {
|
guard let uniformType = registeredTypes.first(where: {
|
||||||
guard let mimeType = $0.preferredMIMEType else { return false }
|
guard let mimeType = $0.preferredMIMEType else { return false }
|
||||||
|
|
||||||
return !Self.unuploadableMimeTypes.contains(mimeType)
|
return Self.uploadableMimeTypes.contains(mimeType)
|
||||||
}),
|
}),
|
||||||
let mimeType = uniformType.preferredMIMEType else {
|
let mimeType = uniformType.preferredMIMEType else {
|
||||||
return Fail(error: MediaProcessingError.invalidMimeType).eraseToAnyPublisher()
|
return Fail(error: MediaProcessingError.invalidMimeType).eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
let type: Attachment.AttachmentType
|
|
||||||
|
|
||||||
if uniformType.conforms(to: .image) {
|
|
||||||
type = .image
|
|
||||||
} else if uniformType.conforms(to: .movie) {
|
|
||||||
type = .video
|
|
||||||
} else if uniformType.conforms(to: .audio) {
|
|
||||||
type = .audio
|
|
||||||
} else if uniformType.conforms(to: .video), uniformType == .mpeg4Movie {
|
|
||||||
type = .gifv
|
|
||||||
} else {
|
|
||||||
type = .unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
return Future<Data, Error> { promise in
|
return Future<Data, Error> { promise in
|
||||||
itemProvider.loadFileRepresentation(forTypeIdentifier: uniformType.identifier) { url, error in
|
itemProvider.loadFileRepresentation(forTypeIdentifier: uniformType.identifier) { url, error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
|
@ -63,13 +48,23 @@ public extension MediaProcessingService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.map { Composition.Attachment(data: $0, type: type, mimeType: mimeType) }
|
.map { (data: $0, mimeType: mimeType) }
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension MediaProcessingService {
|
private extension MediaProcessingService {
|
||||||
static let unuploadableMimeTypes: Set<String> = [UTType.heic.preferredMIMEType!]
|
|
||||||
|
static let uploadableMimeTypes = Set(
|
||||||
|
[UTType.png,
|
||||||
|
UTType.jpeg,
|
||||||
|
UTType.gif,
|
||||||
|
UTType.webP,
|
||||||
|
UTType.mpeg4Movie,
|
||||||
|
UTType.quickTimeMovie,
|
||||||
|
UTType.mp3,
|
||||||
|
UTType.wav]
|
||||||
|
.compactMap(\.preferredMIMEType))
|
||||||
static let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
|
static let imageSourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary
|
||||||
static let thumbnailOptions = [
|
static let thumbnailOptions = [
|
||||||
kCGImageSourceCreateThumbnailFromImageAlways: true,
|
kCGImageSourceCreateThumbnailFromImageAlways: true,
|
||||||
|
|
|
@ -73,9 +73,14 @@ class NewStatusViewController: UICollectionViewController {
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
// Invalidate the collection view layout on anything that could change the height of a cell
|
||||||
viewModel.$compositionViewModels
|
viewModel.$compositionViewModels
|
||||||
.flatMap { Publishers.MergeMany($0.map(\.composition.$text)) }
|
.flatMap { Publishers.MergeMany($0.map(\.composition.$text)) }
|
||||||
.sink { [weak self] _ in self?.collectionView.collectionViewLayout.invalidateLayout() }
|
.map { _ in () }
|
||||||
|
.merge(with: viewModel.$compositionViewModels
|
||||||
|
.flatMap { Publishers.MergeMany($0.map(\.objectWillChange)) }
|
||||||
|
.map { _ in () })
|
||||||
|
.sink { [weak self] in self?.collectionView.collectionViewLayout.invalidateLayout() }
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
viewModel.$canPost.sink { [weak self] in self?.postButton.isEnabled = $0 }.store(in: &cancellables)
|
viewModel.$canPost.sink { [weak self] in self?.postButton.isEnabled = $0 }.store(in: &cancellables)
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import Mastodon
|
||||||
|
import ServiceLayer
|
||||||
|
|
||||||
|
public final class CompositionAttachmentViewModel: ObservableObject {
|
||||||
|
public var attachment: Attachment
|
||||||
|
|
||||||
|
init(attachment: Attachment) {
|
||||||
|
self.attachment = attachment
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,8 +9,10 @@ public final class CompositionViewModel: ObservableObject {
|
||||||
public let composition: Composition
|
public let composition: Composition
|
||||||
@Published public private(set) var isPostable = false
|
@Published public private(set) var isPostable = false
|
||||||
@Published public private(set) var identification: Identification
|
@Published public private(set) var identification: Identification
|
||||||
|
@Published public private(set) var attachmentUpload: AttachmentUpload?
|
||||||
|
|
||||||
private let eventsSubject: PassthroughSubject<Event, Never>
|
private let eventsSubject: PassthroughSubject<Event, Never>
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(composition: Composition,
|
init(composition: Composition,
|
||||||
identification: Identification,
|
identification: Identification,
|
||||||
|
@ -28,7 +30,13 @@ public extension CompositionViewModel {
|
||||||
enum Event {
|
enum Event {
|
||||||
case insertAfter(CompositionViewModel)
|
case insertAfter(CompositionViewModel)
|
||||||
case presentMediaPicker(CompositionViewModel)
|
case presentMediaPicker(CompositionViewModel)
|
||||||
case attach(itemProvider: NSItemProvider, viewModel: CompositionViewModel)
|
case error(Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AttachmentUpload {
|
||||||
|
public let progress: Progress
|
||||||
|
public let data: Data
|
||||||
|
public let mimeType: String
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentMediaPicker() {
|
func presentMediaPicker() {
|
||||||
|
@ -40,6 +48,30 @@ public extension CompositionViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func attach(itemProvider: NSItemProvider) {
|
func attach(itemProvider: NSItemProvider) {
|
||||||
eventsSubject.send(.attach(itemProvider: itemProvider, viewModel: self))
|
let progress = Progress(totalUnitCount: 1)
|
||||||
|
|
||||||
|
MediaProcessingService.dataAndMimeType(itemProvider: itemProvider)
|
||||||
|
.flatMap { [weak self] data, mimeType -> AnyPublisher<Attachment, Error> in
|
||||||
|
guard let self = self else { return Empty().eraseToAnyPublisher() }
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.attachmentUpload = AttachmentUpload(progress: progress, data: data, mimeType: mimeType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.identification.service.uploadAttachment(data: data, mimeType: mimeType, progress: progress)
|
||||||
|
}
|
||||||
|
.print()
|
||||||
|
.sink { [weak self] in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self?.attachmentUpload = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if case let .failure(error) = $0 {
|
||||||
|
self?.eventsSubject.send(.error(error))
|
||||||
|
}
|
||||||
|
} receiveValue: { [weak self] in
|
||||||
|
self?.composition.attachments.append($0)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,11 +99,8 @@ private extension NewStatusViewModel {
|
||||||
} else {
|
} else {
|
||||||
compositionViewModels.insert(newViewModel, at: index + 1)
|
compositionViewModels.insert(newViewModel, at: index + 1)
|
||||||
}
|
}
|
||||||
case let .attach(itemProvider, viewModel):
|
case let .error(error):
|
||||||
MediaProcessingService.attachment(itemProvider: itemProvider)
|
alertItem = AlertItem(error: error)
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
|
||||||
.sink { viewModel.composition.attachments.append($0) }
|
|
||||||
.store(in: &cancellables)
|
|
||||||
default:
|
default:
|
||||||
eventsSubject.send(event)
|
eventsSubject.send(event)
|
||||||
}
|
}
|
||||||
|
|
41
Views/AttachmentUploadView.swift
Normal file
41
Views/AttachmentUploadView.swift
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import UIKit
|
||||||
|
import ViewModels
|
||||||
|
|
||||||
|
final class AttachmentUploadView: UIView {
|
||||||
|
let progressView = UIProgressView(progressViewStyle: .default)
|
||||||
|
private var progressCancellable: AnyCancellable?
|
||||||
|
|
||||||
|
var attachmentUpload: CompositionViewModel.AttachmentUpload? {
|
||||||
|
didSet {
|
||||||
|
if let attachmentUpload = attachmentUpload {
|
||||||
|
progressCancellable = attachmentUpload.progress.publisher(for: \.fractionCompleted)
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink { [weak self] in self?.progressView.progress = Float($0) }
|
||||||
|
isHidden = false
|
||||||
|
} else {
|
||||||
|
isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
addSubview(progressView)
|
||||||
|
progressView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
progressView.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
|
||||||
|
progressView.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
|
||||||
|
progressView.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
7
Views/CompositionAttachmentCollectionViewCell.swift
Normal file
7
Views/CompositionAttachmentCollectionViewCell.swift
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
// Copyright © 2020 Metabolist. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class CompositionAttachmentCollectionViewCell: UICollectionViewCell {
|
||||||
|
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ import UIKit
|
||||||
class CompositionView: UIView {
|
class CompositionView: UIView {
|
||||||
let avatarImageView = UIImageView()
|
let avatarImageView = UIImageView()
|
||||||
let textView = UITextView()
|
let textView = UITextView()
|
||||||
|
let attachmentUploadView = AttachmentUploadView()
|
||||||
|
// let attachmentsCollectionView = UICollectionView()
|
||||||
|
|
||||||
private var compositionConfiguration: CompositionContentConfiguration
|
private var compositionConfiguration: CompositionContentConfiguration
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
@ -46,6 +48,8 @@ extension CompositionView: UITextViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension CompositionView {
|
private extension CompositionView {
|
||||||
|
static let attachmentsCollectionViewHeight: CGFloat = 100
|
||||||
|
|
||||||
func initialSetup() {
|
func initialSetup() {
|
||||||
addSubview(avatarImageView)
|
addSubview(avatarImageView)
|
||||||
avatarImageView.translatesAutoresizingMaskIntoConstraints = false
|
avatarImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
@ -67,6 +71,10 @@ private extension CompositionView {
|
||||||
textView.inputAccessoryView?.sizeToFit()
|
textView.inputAccessoryView?.sizeToFit()
|
||||||
textView.delegate = self
|
textView.delegate = self
|
||||||
|
|
||||||
|
// stackView.addArrangedSubview(attachmentsCollectionView)
|
||||||
|
|
||||||
|
stackView.addArrangedSubview(attachmentUploadView)
|
||||||
|
|
||||||
let constraints = [
|
let constraints = [
|
||||||
avatarImageView.heightAnchor.constraint(equalToConstant: .avatarDimension),
|
avatarImageView.heightAnchor.constraint(equalToConstant: .avatarDimension),
|
||||||
avatarImageView.widthAnchor.constraint(equalToConstant: .avatarDimension),
|
avatarImageView.widthAnchor.constraint(equalToConstant: .avatarDimension),
|
||||||
|
@ -76,7 +84,9 @@ private extension CompositionView {
|
||||||
stackView.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: .defaultSpacing),
|
stackView.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: .defaultSpacing),
|
||||||
stackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor),
|
stackView.topAnchor.constraint(equalTo: readableContentGuide.topAnchor),
|
||||||
stackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
|
stackView.trailingAnchor.constraint(equalTo: readableContentGuide.trailingAnchor),
|
||||||
stackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor)
|
stackView.bottomAnchor.constraint(equalTo: readableContentGuide.bottomAnchor),
|
||||||
|
// attachmentsCollectionView.heightAnchor.constraint(equalToConstant: Self.attachmentsCollectionViewHeight)
|
||||||
|
attachmentUploadView.heightAnchor.constraint(equalToConstant: Self.attachmentsCollectionViewHeight)
|
||||||
]
|
]
|
||||||
|
|
||||||
for constraint in constraints {
|
for constraint in constraints {
|
||||||
|
@ -88,6 +98,10 @@ private extension CompositionView {
|
||||||
compositionConfiguration.viewModel.$identification.map(\.identity.image)
|
compositionConfiguration.viewModel.$identification.map(\.identity.image)
|
||||||
.sink { [weak self] in self?.avatarImageView.kf.setImage(with: $0) }
|
.sink { [weak self] in self?.avatarImageView.kf.setImage(with: $0) }
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
compositionConfiguration.viewModel.$attachmentUpload
|
||||||
|
.sink { [weak self] in self?.attachmentUploadView.attachmentUpload = $0 }
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
func applyCompositionConfiguration() {
|
func applyCompositionConfiguration() {
|
||||||
|
|
Loading…
Reference in a new issue