Show edited attachments in gallery
This commit is contained in:
parent
f102ebbfc8
commit
abbe0f82e6
@ -10,6 +10,7 @@ import Pachyderm
|
|||||||
import PhotosUI
|
import PhotosUI
|
||||||
import PencilKit
|
import PencilKit
|
||||||
import TuskerComponents
|
import TuskerComponents
|
||||||
|
import GalleryVC
|
||||||
|
|
||||||
// Configuration/data injected from outside the compose UI.
|
// Configuration/data injected from outside the compose UI.
|
||||||
public struct ComposeUIConfig {
|
public struct ComposeUIConfig {
|
||||||
@ -38,6 +39,8 @@ public struct ComposeUIConfig {
|
|||||||
public var fetchAvatar: AvatarImageView.FetchAvatar = { _ in nil }
|
public var fetchAvatar: AvatarImageView.FetchAvatar = { _ in nil }
|
||||||
public var displayNameLabel: (any AccountProtocol, Font.TextStyle, CGFloat) -> AnyView = { _, _, _ in AnyView(EmptyView()) }
|
public var displayNameLabel: (any AccountProtocol, Font.TextStyle, CGFloat) -> AnyView = { _, _, _ in AnyView(EmptyView()) }
|
||||||
public var replyContentView: (any StatusProtocol, @escaping (CGFloat) -> Void) -> AnyView = { _, _ in AnyView(EmptyView()) }
|
public var replyContentView: (any StatusProtocol, @escaping (CGFloat) -> Void) -> AnyView = { _, _ in AnyView(EmptyView()) }
|
||||||
|
public var fetchImageAndGIFData: (URL) async -> (UIImage, Data)? = { _ in nil }
|
||||||
|
public var makeGifvGalleryContentVC: (URL) -> (any GalleryContentViewController)? = { _ in nil }
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ private struct AttachmentThumbnailViewContent: View {
|
|||||||
var contentMode: ContentMode = .fit
|
var contentMode: ContentMode = .fit
|
||||||
var thumbnailSize: CGSize?
|
var thumbnailSize: CGSize?
|
||||||
@State private var mode: Mode = .empty
|
@State private var mode: Mode = .empty
|
||||||
@EnvironmentObject private var composeController: ComposeController
|
@Environment(\.composeUIConfig.fetchImageAndGIFData) private var fetchImageAndGIFData
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
switch mode {
|
switch mode {
|
||||||
@ -56,7 +56,7 @@ private struct AttachmentThumbnailViewContent: View {
|
|||||||
case .editing(_, let kind, let url):
|
case .editing(_, let kind, let url):
|
||||||
switch kind {
|
switch kind {
|
||||||
case .image:
|
case .image:
|
||||||
if let image = await composeController.fetchAttachment(url) {
|
if let (image, _) = await fetchImageAndGIFData(url) {
|
||||||
self.mode = .image(image)
|
self.mode = .image(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// AttachmentGalleryDataSource.swift
|
// AttachmentsGalleryDataSource.swift
|
||||||
// ComposeUI
|
// ComposeUI
|
||||||
//
|
//
|
||||||
// Created by Shadowfacts on 11/21/24.
|
// Created by Shadowfacts on 11/21/24.
|
||||||
@ -12,6 +12,8 @@ import Photos
|
|||||||
|
|
||||||
struct AttachmentsGalleryDataSource: GalleryDataSource {
|
struct AttachmentsGalleryDataSource: GalleryDataSource {
|
||||||
let collectionView: UICollectionView
|
let collectionView: UICollectionView
|
||||||
|
let fetchImageAndGIFData: (URL) async -> (UIImage, Data)?
|
||||||
|
let makeGifvGalleryContentVC: (URL) -> (any GalleryContentViewController)?
|
||||||
let attachmentAtIndex: (Int) -> DraftAttachment?
|
let attachmentAtIndex: (Int) -> DraftAttachment?
|
||||||
|
|
||||||
func galleryItemsCount() -> Int {
|
func galleryItemsCount() -> Int {
|
||||||
@ -23,12 +25,32 @@ struct AttachmentsGalleryDataSource: GalleryDataSource {
|
|||||||
|
|
||||||
let content: any GalleryContentViewController
|
let content: any GalleryContentViewController
|
||||||
switch attachment.data {
|
switch attachment.data {
|
||||||
case .editing(_, _, _):
|
case .editing(_, let kind, let url):
|
||||||
fatalError("TODO")
|
switch kind {
|
||||||
|
case .image:
|
||||||
|
content = LoadingGalleryContentViewController(caption: nil) {
|
||||||
|
if let (image, data) = await fetchImageAndGIFData(url) {
|
||||||
|
let gifController: GIFController? = if url.pathExtension == "gif" {
|
||||||
|
GIFController(gifData: data)
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
return ImageGalleryContentViewController(image: image, caption: nil, gifController: gifController)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .video, .audio:
|
||||||
|
content = VideoGalleryContentViewController(url: url, caption: nil)
|
||||||
|
case .gifv:
|
||||||
|
content = LoadingGalleryContentViewController(caption: nil) { makeGifvGalleryContentVC(url) }
|
||||||
|
case .unknown:
|
||||||
|
content = LoadingGalleryContentViewController(caption: nil) { nil }
|
||||||
|
}
|
||||||
|
|
||||||
case .asset(let id):
|
case .asset(let id):
|
||||||
content = LoadingGalleryContentViewController(caption: nil) {
|
content = LoadingGalleryContentViewController(caption: nil) {
|
||||||
if let (image, gifData) = await fetchImageAndGIFData(assetID: id) {
|
if let (image, gifData) = await fetchAssetImageAndGIFData(assetID: id) {
|
||||||
let gifController = gifData.map(GIFController.init)
|
let gifController = gifData.map(GIFController.init)
|
||||||
return ImageGalleryContentViewController(image: image, caption: nil, gifController: gifController)
|
return ImageGalleryContentViewController(image: image, caption: nil, gifController: gifController)
|
||||||
} else {
|
} else {
|
||||||
@ -72,7 +94,7 @@ struct AttachmentsGalleryDataSource: GalleryDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func fetchImageAndGIFData(assetID id: String) async -> (UIImage, Data?)? {
|
private func fetchAssetImageAndGIFData(assetID id: String) async -> (UIImage, Data?)? {
|
||||||
guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [id], options: nil).firstObject else {
|
guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [id], options: nil).firstObject else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,16 @@ private struct WrappedCollectionView: UIViewControllerRepresentable {
|
|||||||
@ObservedObject var draft: Draft
|
@ObservedObject var draft: Draft
|
||||||
let spacing: CGFloat
|
let spacing: CGFloat
|
||||||
let minItemSize: CGFloat
|
let minItemSize: CGFloat
|
||||||
|
@Environment(\.composeUIConfig.fetchImageAndGIFData) private var fetchImageAndGIFData
|
||||||
|
@Environment(\.composeUIConfig.makeGifvGalleryContentVC) private var makeGifvGalleryContentVC
|
||||||
|
|
||||||
func makeUIViewController(context: Context) -> WrappedCollectionViewController {
|
func makeUIViewController(context: Context) -> WrappedCollectionViewController {
|
||||||
WrappedCollectionViewController(spacing: spacing, minItemSize: minItemSize)
|
WrappedCollectionViewController(
|
||||||
|
spacing: spacing,
|
||||||
|
minItemSize: minItemSize,
|
||||||
|
fetchImageAndGIFData: fetchImageAndGIFData,
|
||||||
|
makeGifvGalleryContentVC: makeGifvGalleryContentVC
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
|
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
|
||||||
@ -167,14 +174,23 @@ private class WrappedCollectionViewController: UIViewController {
|
|||||||
fileprivate var currentInteractiveMoveStartOffsetInCell: CGPoint?
|
fileprivate var currentInteractiveMoveStartOffsetInCell: CGPoint?
|
||||||
fileprivate var currentInteractiveMoveCell: HostingCollectionViewCell?
|
fileprivate var currentInteractiveMoveCell: HostingCollectionViewCell?
|
||||||
fileprivate var addAttachment: ((DraftAttachment) -> Void)? = nil
|
fileprivate var addAttachment: ((DraftAttachment) -> Void)? = nil
|
||||||
|
fileprivate var fetchImageAndGIFData: (URL) async -> (UIImage, Data)?
|
||||||
|
fileprivate var makeGifvGalleryContentVC: (URL) -> (any GalleryContentViewController)?
|
||||||
|
|
||||||
var collectionView: UICollectionView {
|
var collectionView: UICollectionView {
|
||||||
view as! UICollectionView
|
view as! UICollectionView
|
||||||
}
|
}
|
||||||
|
|
||||||
init(spacing: CGFloat, minItemSize: CGFloat) {
|
init(
|
||||||
|
spacing: CGFloat,
|
||||||
|
minItemSize: CGFloat,
|
||||||
|
fetchImageAndGIFData: @escaping (URL) async -> (UIImage, Data)?,
|
||||||
|
makeGifvGalleryContentVC: @escaping (URL) -> (any GalleryContentViewController)?
|
||||||
|
) {
|
||||||
self.spacing = spacing
|
self.spacing = spacing
|
||||||
self.minItemSize = minItemSize
|
self.minItemSize = minItemSize
|
||||||
|
self.fetchImageAndGIFData = fetchImageAndGIFData
|
||||||
|
self.makeGifvGalleryContentVC = makeGifvGalleryContentVC
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,7 +346,11 @@ extension WrappedCollectionViewController: UICollectionViewDelegate {
|
|||||||
guard case .attachment(_) = dataSource.itemIdentifier(for: indexPath) else {
|
guard case .attachment(_) = dataSource.itemIdentifier(for: indexPath) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let dataSource = AttachmentsGalleryDataSource(collectionView: collectionView) { [dataSource] in
|
let dataSource = AttachmentsGalleryDataSource(
|
||||||
|
collectionView: collectionView,
|
||||||
|
fetchImageAndGIFData: self.fetchImageAndGIFData,
|
||||||
|
makeGifvGalleryContentVC: self.makeGifvGalleryContentVC
|
||||||
|
) { [dataSource] in
|
||||||
let item = dataSource?.itemIdentifier(for: IndexPath(item: $0, section: 0))
|
let item = dataSource?.itemIdentifier(for: IndexPath(item: $0, section: 0))
|
||||||
switch item {
|
switch item {
|
||||||
case .attachment(let attachment):
|
case .attachment(let attachment):
|
||||||
|
@ -55,7 +55,7 @@ public class LoadingGalleryContentViewController: UIViewController, GalleryConte
|
|||||||
|
|
||||||
container?.setGalleryContentLoading(true)
|
container?.setGalleryContentLoading(true)
|
||||||
|
|
||||||
Task {
|
Task { @MainActor in
|
||||||
if let wrapped = await provider() {
|
if let wrapped = await provider() {
|
||||||
self.wrapped = wrapped
|
self.wrapped = wrapped
|
||||||
wrapped.container = container
|
wrapped.container = container
|
||||||
|
@ -104,6 +104,18 @@ class ComposeHostingController: UIHostingController<ComposeHostingController.Vie
|
|||||||
config.fetchAvatar = { @MainActor in await ImageCache.avatars.get($0).1 }
|
config.fetchAvatar = { @MainActor in await ImageCache.avatars.get($0).1 }
|
||||||
config.displayNameLabel = { AnyView(AccountDisplayNameView(account: $0, textStyle: $1, emojiSize: $2)) }
|
config.displayNameLabel = { AnyView(AccountDisplayNameView(account: $0, textStyle: $1, emojiSize: $2)) }
|
||||||
config.replyContentView = { [mastodonController] in AnyView(ComposeReplyContentView(status: $0, mastodonController: mastodonController, heightChanged: $1)) }
|
config.replyContentView = { [mastodonController] in AnyView(ComposeReplyContentView(status: $0, mastodonController: mastodonController, heightChanged: $1)) }
|
||||||
|
config.fetchImageAndGIFData = {
|
||||||
|
if case let (.some(data), .some(image)) = await ImageCache.attachments.get($0) {
|
||||||
|
return (image, data)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.makeGifvGalleryContentVC = {
|
||||||
|
let asset = AVAsset(url: $0)
|
||||||
|
let controller = GifvController(asset: asset)
|
||||||
|
return GifvGalleryContentViewController(controller: controller, url: $0, caption: nil)
|
||||||
|
}
|
||||||
|
|
||||||
self.config = config
|
self.config = config
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user