2022-01-27 14:23:39 +01:00
//
// D a t a S o u r c e F a c a d e + M e d i a . s w i f t
// M a s t o d o n
//
// C r e a t e d b y M a i n a s u K o n 2 0 2 2 - 1 - 2 6 .
//
2022-07-14 21:29:04 +02:00
import os . log
2022-01-27 14:23:39 +01:00
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
)
2022-11-17 23:02:43 +01:00
_ = dependency . coordinator . present (
2022-01-27 14:23:39 +01:00
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 {
2022-07-14 21:29:04 +02:00
let logger = Logger ( subsystem : " DataSourceFacade " , category : " Media " )
2022-01-27 14:23:39 +01:00
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 :
2022-07-14 21:29:04 +02:00
return . profileBanner ( . init (
2022-01-27 14:23:39 +01:00
assetURL : _headerAssetURL ,
thumbnail : thumbnail
) )
}
} ( )
2022-07-14 21:29:04 +02:00
guard mediaPreviewItem . isAssetURLValid else {
logger . log ( level : . debug , " \( ( #file as NSString ) . lastPathComponent , privacy : . public ) [ \( #line , privacy : . public ) ], \( #function , privacy : . public ) : discard preview due to assetURL invalid " )
return
}
2022-01-27 14:23:39 +01:00
coordinateToMediaPreviewScene (
dependency : dependency ,
mediaPreviewItem : mediaPreviewItem ,
mediaPreviewTransitionItem : mediaPreviewTransitionItem
)
}
}