2021-04-28 09:02:34 +02:00
|
|
|
//
|
|
|
|
// MediaPreviewViewModel.swift
|
|
|
|
// Mastodon
|
|
|
|
//
|
|
|
|
// Created by MainasuK Cirno on 2021-4-28.
|
|
|
|
//
|
|
|
|
|
|
|
|
import UIKit
|
|
|
|
import Combine
|
|
|
|
import CoreData
|
|
|
|
import CoreDataStack
|
|
|
|
import Pageboy
|
2022-10-08 07:43:06 +02:00
|
|
|
import MastodonCore
|
2021-04-28 09:02:34 +02:00
|
|
|
|
2022-11-15 03:52:16 +01:00
|
|
|
protocol MediaPreviewPage: UIViewController {
|
|
|
|
func setShowingChrome(_ showingChrome: Bool)
|
|
|
|
}
|
|
|
|
|
2021-04-28 09:02:34 +02:00
|
|
|
final class MediaPreviewViewModel: NSObject {
|
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
weak var mediaPreviewImageViewControllerDelegate: MediaPreviewImageViewControllerDelegate?
|
|
|
|
|
2021-04-28 09:02:34 +02:00
|
|
|
// input
|
|
|
|
let context: AppContext
|
2022-01-27 14:23:39 +01:00
|
|
|
let item: PreviewItem
|
|
|
|
let transitionItem: MediaPreviewTransitionItem
|
|
|
|
|
|
|
|
@Published var currentPage: Int
|
2022-11-15 03:52:16 +01:00
|
|
|
@Published var showingChrome = true
|
2022-11-26 21:11:13 +01:00
|
|
|
@Published var altText: String?
|
2022-11-26 15:39:24 +01:00
|
|
|
|
2021-04-28 09:02:34 +02:00
|
|
|
// output
|
2022-11-26 15:39:24 +01:00
|
|
|
let viewControllers: [MediaPreviewPage]
|
2022-11-15 03:52:16 +01:00
|
|
|
|
|
|
|
private var disposeBag: Set<AnyCancellable> = []
|
2021-04-28 09:02:34 +02:00
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
init(
|
|
|
|
context: AppContext,
|
|
|
|
item: PreviewItem,
|
|
|
|
transitionItem: MediaPreviewTransitionItem
|
|
|
|
) {
|
2021-04-28 09:02:34 +02:00
|
|
|
self.context = context
|
2022-01-27 14:23:39 +01:00
|
|
|
self.item = item
|
|
|
|
var currentPage = 0
|
2022-11-15 03:52:16 +01:00
|
|
|
var viewControllers: [MediaPreviewPage] = []
|
2022-11-26 21:11:13 +01:00
|
|
|
var getAltText = { (page: Int) -> String? in nil }
|
2022-01-27 14:23:39 +01:00
|
|
|
switch item {
|
|
|
|
case .attachment(let previewContext):
|
2022-11-26 21:11:13 +01:00
|
|
|
getAltText = { previewContext.attachments[$0].altDescription }
|
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
currentPage = previewContext.initialIndex
|
|
|
|
for (i, attachment) in previewContext.attachments.enumerated() {
|
2022-02-09 13:35:19 +01:00
|
|
|
switch attachment.kind {
|
|
|
|
case .image:
|
|
|
|
let viewController = MediaPreviewImageViewController()
|
|
|
|
let viewModel = MediaPreviewImageViewModel(
|
|
|
|
context: context,
|
2022-11-26 15:38:14 +01:00
|
|
|
item: .init(
|
2022-02-09 13:35:19 +01:00
|
|
|
assetURL: attachment.assetURL.flatMap { URL(string: $0) },
|
|
|
|
thumbnail: previewContext.thumbnail(at: i),
|
|
|
|
altText: attachment.altDescription
|
2022-11-26 15:38:14 +01:00
|
|
|
)
|
2022-02-09 13:35:19 +01:00
|
|
|
)
|
|
|
|
viewController.viewModel = viewModel
|
|
|
|
viewControllers.append(viewController)
|
|
|
|
case .gifv:
|
|
|
|
let viewController = MediaPreviewVideoViewController()
|
|
|
|
let viewModel = MediaPreviewVideoViewModel(
|
|
|
|
context: context,
|
|
|
|
item: .gif(.init(
|
|
|
|
assetURL: attachment.assetURL.flatMap { URL(string: $0) },
|
2022-11-26 15:39:24 +01:00
|
|
|
previewURL: attachment.previewURL.flatMap { URL(string: $0) },
|
|
|
|
altText: attachment.altDescription
|
2022-02-09 13:35:19 +01:00
|
|
|
))
|
|
|
|
)
|
|
|
|
viewController.viewModel = viewModel
|
|
|
|
viewControllers.append(viewController)
|
|
|
|
case .video, .audio:
|
|
|
|
let viewController = MediaPreviewVideoViewController()
|
|
|
|
let viewModel = MediaPreviewVideoViewModel(
|
|
|
|
context: context,
|
|
|
|
item: .video(.init(
|
|
|
|
assetURL: attachment.assetURL.flatMap { URL(string: $0) },
|
2022-11-26 15:39:24 +01:00
|
|
|
previewURL: attachment.previewURL.flatMap { URL(string: $0) },
|
|
|
|
altText: attachment.altDescription
|
2022-02-09 13:35:19 +01:00
|
|
|
))
|
|
|
|
)
|
|
|
|
viewController.viewModel = viewModel
|
|
|
|
viewControllers.append(viewController)
|
|
|
|
} // end switch attachment.kind { … }
|
2022-01-27 14:23:39 +01:00
|
|
|
} // end for … in …
|
|
|
|
case .profileAvatar(let previewContext):
|
|
|
|
let viewController = MediaPreviewImageViewController()
|
|
|
|
let viewModel = MediaPreviewImageViewModel(
|
|
|
|
context: context,
|
2022-11-26 15:38:14 +01:00
|
|
|
item: .init(
|
2022-01-27 14:23:39 +01:00
|
|
|
assetURL: previewContext.assetURL.flatMap { URL(string: $0) },
|
|
|
|
thumbnail: previewContext.thumbnail,
|
|
|
|
altText: nil
|
2022-11-26 15:38:14 +01:00
|
|
|
)
|
2022-01-27 14:23:39 +01:00
|
|
|
)
|
|
|
|
viewController.viewModel = viewModel
|
|
|
|
viewControllers.append(viewController)
|
|
|
|
case .profileBanner(let previewContext):
|
|
|
|
let viewController = MediaPreviewImageViewController()
|
|
|
|
let viewModel = MediaPreviewImageViewModel(
|
|
|
|
context: context,
|
2022-11-26 15:38:14 +01:00
|
|
|
item: .init(
|
2022-01-27 14:23:39 +01:00
|
|
|
assetURL: previewContext.assetURL.flatMap { URL(string: $0) },
|
|
|
|
thumbnail: previewContext.thumbnail,
|
|
|
|
altText: nil
|
2022-11-26 15:38:14 +01:00
|
|
|
)
|
2022-01-27 14:23:39 +01:00
|
|
|
)
|
|
|
|
viewController.viewModel = viewModel
|
|
|
|
viewControllers.append(viewController)
|
|
|
|
} // end switch
|
2022-02-09 13:35:19 +01:00
|
|
|
|
2021-04-28 09:02:34 +02:00
|
|
|
self.viewControllers = viewControllers
|
2022-01-27 14:23:39 +01:00
|
|
|
self.currentPage = currentPage
|
|
|
|
self.transitionItem = transitionItem
|
2021-04-28 09:02:34 +02:00
|
|
|
super.init()
|
2022-11-15 03:52:16 +01:00
|
|
|
|
2022-11-26 21:11:13 +01:00
|
|
|
self.$currentPage
|
|
|
|
.map(getAltText)
|
|
|
|
.assign(to: &$altText)
|
|
|
|
|
2022-11-15 03:52:16 +01:00
|
|
|
for viewController in viewControllers {
|
|
|
|
self.$showingChrome
|
|
|
|
.sink { [weak viewController] showingChrome in
|
|
|
|
viewController?.setShowingChrome(showingChrome)
|
|
|
|
}
|
|
|
|
.store(in: &disposeBag)
|
|
|
|
}
|
2021-04-28 09:02:34 +02:00
|
|
|
}
|
2022-02-09 13:35:19 +01:00
|
|
|
|
2021-04-28 09:02:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
extension MediaPreviewViewModel {
|
|
|
|
|
|
|
|
enum PreviewItem {
|
2022-01-27 14:23:39 +01:00
|
|
|
case attachment(AttachmentPreviewContext)
|
|
|
|
case profileAvatar(ProfileAvatarPreviewContext)
|
|
|
|
case profileBanner(ProfileBannerPreviewContext)
|
|
|
|
// case local(LocalImagePreviewMeta)
|
2022-07-14 21:29:04 +02:00
|
|
|
|
|
|
|
var isAssetURLValid: Bool {
|
|
|
|
switch self {
|
|
|
|
case .attachment:
|
|
|
|
return true // default valid
|
|
|
|
case .profileAvatar:
|
|
|
|
return true // default valid
|
|
|
|
case .profileBanner(let item):
|
|
|
|
guard let assertURL = item.assetURL else { return false }
|
|
|
|
guard !assertURL.hasSuffix("missing.png") else { return false }
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
2021-04-28 09:02:34 +02:00
|
|
|
}
|
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
struct AttachmentPreviewContext {
|
|
|
|
let attachments: [MastodonAttachment]
|
2021-04-28 09:02:34 +02:00
|
|
|
let initialIndex: Int
|
2022-01-27 14:23:39 +01:00
|
|
|
let thumbnails: [UIImage?]
|
|
|
|
|
|
|
|
func thumbnail(at index: Int) -> UIImage? {
|
|
|
|
guard index < thumbnails.count else { return nil }
|
|
|
|
return thumbnails[index]
|
|
|
|
}
|
2021-04-28 14:36:10 +02:00
|
|
|
}
|
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
struct ProfileAvatarPreviewContext {
|
|
|
|
let assetURL: String?
|
|
|
|
let thumbnail: UIImage?
|
2021-04-28 14:10:17 +02:00
|
|
|
}
|
2022-01-27 14:23:39 +01:00
|
|
|
|
|
|
|
struct ProfileBannerPreviewContext {
|
|
|
|
let assetURL: String?
|
|
|
|
let thumbnail: UIImage?
|
2021-04-28 09:02:34 +02:00
|
|
|
}
|
2022-01-27 14:23:39 +01:00
|
|
|
|
|
|
|
// struct LocalImagePreviewMeta {
|
|
|
|
// let image: UIImage
|
|
|
|
// }
|
2021-04-28 09:02:34 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - PageboyViewControllerDataSource
|
|
|
|
extension MediaPreviewViewModel: PageboyViewControllerDataSource {
|
|
|
|
|
|
|
|
func numberOfViewControllers(in pageboyViewController: PageboyViewController) -> Int {
|
|
|
|
return viewControllers.count
|
|
|
|
}
|
|
|
|
|
|
|
|
func viewController(for pageboyViewController: PageboyViewController, at index: PageboyViewController.PageIndex) -> UIViewController? {
|
|
|
|
let viewController = viewControllers[index]
|
|
|
|
if let mediaPreviewImageViewController = viewController as? MediaPreviewImageViewController {
|
|
|
|
mediaPreviewImageViewController.delegate = mediaPreviewImageViewControllerDelegate
|
|
|
|
}
|
|
|
|
return viewController
|
|
|
|
}
|
|
|
|
|
|
|
|
func defaultPage(for pageboyViewController: PageboyViewController) -> PageboyViewController.Page? {
|
2022-01-27 14:23:39 +01:00
|
|
|
guard case let .attachment(previewContext) = item else { return nil }
|
|
|
|
return .at(index: previewContext.initialIndex)
|
2021-04-28 09:02:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|