mastodon-ios/Mastodon/Protocol/Provider/DataSourceFacade+Media.swift

238 lines
7.6 KiB
Swift

//
// DataSourceFacade+Media.swift
// Mastodon
//
// Created by MainasuK on 2022-1-26.
//
import UIKit
import CoreDataStack
import MastodonUI
import MastodonLocalization
extension DataSourceFacade {
@MainActor
static func coordinateToMediaPreviewScene(
dependency: NeedsDependency & MediaPreviewableViewController,
mediaPreviewItem: MediaPreviewViewModel.PreviewItem,
mediaPreviewTransitionItem: MediaPreviewTransitionItem
) {
let mediaPreviewViewModel = MediaPreviewViewModel(
context: dependency.context,
item: mediaPreviewItem,
transitionItem: mediaPreviewTransitionItem
)
_ = dependency.coordinator.present(
scene: .mediaPreview(viewModel: mediaPreviewViewModel),
from: dependency,
transition: .custom(transitioningDelegate: dependency.mediaPreviewTransitionController)
)
}
}
extension DataSourceFacade {
struct AttachmentPreviewContext {
let containerView: ContainerView
let mediaView: MediaView
let index: Int
enum ContainerView {
case mediaView(MediaView)
case mediaGridContainerView(MediaGridContainerView)
}
func thumbnails() async -> [UIImage?] {
switch containerView {
case .mediaView(let mediaView):
let thumbnail = await mediaView.thumbnail()
return [thumbnail]
case .mediaGridContainerView(let mediaGridContainerView):
let thumbnails = await mediaGridContainerView.mediaViews.parallelMap { mediaView in
return await mediaView.thumbnail()
}
return thumbnails
}
}
}
@MainActor
static func coordinateToMediaPreviewScene(
dependency: NeedsDependency & MediaPreviewableViewController,
status: ManagedObjectRecord<Status>,
previewContext: AttachmentPreviewContext
) async throws {
let managedObjectContext = dependency.context.managedObjectContext
let attachments: [MastodonAttachment] = try await managedObjectContext.perform {
guard let _status = status.object(in: managedObjectContext) else { return [] }
let status = _status.reblog ?? _status
return status.attachments
}
let thumbnails = await previewContext.thumbnails()
let _source: MediaPreviewTransitionItem.Source? = {
switch previewContext.containerView {
case .mediaView(let mediaView):
return .attachment(mediaView)
case .mediaGridContainerView(let mediaGridContainerView):
return .attachments(mediaGridContainerView)
}
}()
guard let source = _source else {
return
}
let mediaPreviewTransitionItem: MediaPreviewTransitionItem = {
let item = MediaPreviewTransitionItem(
source: source,
previewableViewController: dependency
)
let mediaView = previewContext.mediaView
item.initialFrame = {
let initialFrame = mediaView.superview!.convert(mediaView.frame, to: nil)
assert(initialFrame != .zero)
return initialFrame
}()
let thumbnail = mediaView.thumbnail()
item.image = thumbnail
item.aspectRatio = {
if let thumbnail = thumbnail {
return thumbnail.size
}
let index = previewContext.index
guard index < attachments.count else { return nil }
let size = attachments[index].size
return size
}()
return item
}()
let mediaPreviewItem = MediaPreviewViewModel.PreviewItem.attachment(.init(
attachments: attachments,
initialIndex: previewContext.index,
thumbnails: thumbnails
))
coordinateToMediaPreviewScene(
dependency: dependency,
mediaPreviewItem: mediaPreviewItem,
mediaPreviewTransitionItem: mediaPreviewTransitionItem
)
}
}
extension DataSourceFacade {
struct ImagePreviewContext {
let imageView: UIImageView
let containerView: ContainerView
enum ContainerView {
case profileAvatar(ProfileHeaderView)
case profileBanner(ProfileHeaderView)
}
func thumbnail() async -> UIImage? {
return await imageView.image
}
}
@MainActor
static func coordinateToMediaPreviewScene(
dependency: NeedsDependency & MediaPreviewableViewController,
user: ManagedObjectRecord<MastodonUser>,
previewContext: ImagePreviewContext
) async throws {
let managedObjectContext = dependency.context.managedObjectContext
var _avatarAssetURL: String?
var _headerAssetURL: String?
try await managedObjectContext.perform {
guard let user = user.object(in: managedObjectContext) else { return }
_avatarAssetURL = user.avatar
_headerAssetURL = user.header
}
let thumbnail = await previewContext.thumbnail()
let source: MediaPreviewTransitionItem.Source = {
switch previewContext.containerView {
case .profileAvatar(let view): return .profileAvatar(view)
case .profileBanner(let view): return .profileBanner(view)
}
}()
let mediaPreviewTransitionItem: MediaPreviewTransitionItem = {
let item = MediaPreviewTransitionItem(
source: source,
previewableViewController: dependency
)
let imageView = previewContext.imageView
item.initialFrame = {
let initialFrame = imageView.superview!.convert(imageView.frame, to: nil)
assert(initialFrame != .zero)
return initialFrame
}()
item.image = thumbnail
item.aspectRatio = {
if let thumbnail = thumbnail {
return thumbnail.size
}
return CGSize(width: 100, height: 100)
}()
item.sourceImageViewCornerRadius = {
switch previewContext.containerView {
case .profileAvatar:
return ProfileHeaderView.avatarImageViewCornerRadius
case .profileBanner:
return 0
}
}()
return item
}()
let mediaPreviewItem: MediaPreviewViewModel.PreviewItem = {
switch previewContext.containerView {
case .profileAvatar:
return .profileAvatar(.init(
assetURL: _avatarAssetURL,
thumbnail: thumbnail
))
case .profileBanner:
return .profileBanner(.init(
assetURL: _headerAssetURL,
thumbnail: thumbnail
))
}
}()
guard mediaPreviewItem.isAssetURLValid else {
return
}
coordinateToMediaPreviewScene(
dependency: dependency,
mediaPreviewItem: mediaPreviewItem,
mediaPreviewTransitionItem: mediaPreviewTransitionItem
)
}
}