Skip to content

Commit

Permalink
Add all of the loaded items for previewing in the preview controller.
Browse files Browse the repository at this point in the history
  • Loading branch information
pixlwave committed Dec 18, 2024
1 parent 6ab2441 commit 577c491
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ struct TimelineMediaPreviewContext {
let viewModel: TimelineViewModelProtocol
/// The namespace that the navigation transition's `sourceID` should be defined in.
let namespace: Namespace.ID
/// A completion to be called immediately *after* the preview has been dismissed.
/// A closure to be called whenever a different preview item is shown. It should also
/// be called *after* the preview has been dismissed, with an ID of `nil`.
///
/// This helps work around a bug caused by the flipped scrollview where the zoomed
/// thumbnail starts off upside down while loading the preview screen.
var completion: (() -> Void)?
var itemIDHandler: ((TimelineItemIdentifier?) -> Void)?
}

struct TimelineMediaPreviewCoordinatorParameters {
Expand Down Expand Up @@ -75,6 +76,6 @@ final class TimelineMediaPreviewCoordinator: CoordinatorProtocol {
// Calling the completion onDisappear isn't ideal, but we don't push away from the screen so it should be
// a good enough approximation of didDismiss, given that the only other option is our navigation callbacks
// which are essentially willDismiss callbacks and happen too early for this particular completion handler.
AnyView(TimelineMediaPreviewScreen(context: viewModel.context, onDisappear: parameters.context.completion))
AnyView(TimelineMediaPreviewScreen(context: viewModel.context, itemIDHandler: parameters.context.itemIDHandler))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,19 @@ enum TimelineMediaPreviewViewModelAction: Equatable {
}

struct TimelineMediaPreviewViewState: BindableState {
/// All of the items in the timeline that can be previewed.
var previewItems: [TimelineMediaPreviewItem]
/// The index of the initial item inside of `previewItems` that is to be shown.
let initialItemIndex: Int

/// The media item that is currently being previewed.
var currentItem: TimelineMediaPreviewItem
/// All of the available actions for the current item.
var currentItemActions: TimelineItemMenuActions?

/// The namespace used for the zoom transition.
let transitionNamespace: Namespace.ID
/// A publisher that the view model uses to signal to the QLPreviewController when the current item has been loaded.
let fileLoadedPublisher = PassthroughSubject<TimelineItemIdentifier, Never>()

var bindings = TimelineMediaPreviewViewStateBindings()
Expand Down Expand Up @@ -49,6 +57,21 @@ class TimelineMediaPreviewItem: NSObject, QLPreviewItem, Identifiable {
self.timelineItem = timelineItem
}

init?(roomTimelineItemViewState: RoomTimelineItemViewState) {
switch roomTimelineItemViewState.type {
case .audio(let audioRoomTimelineItem):
timelineItem = audioRoomTimelineItem
case .file(let fileRoomTimelineItem):
timelineItem = fileRoomTimelineItem
case .image(let imageRoomTimelineItem):
timelineItem = imageRoomTimelineItem
case .video(let videoRoomTimelineItem):
timelineItem = videoRoomTimelineItem
default:
return nil
}
}

// MARK: Identifiable

var id: TimelineItemIdentifier { timelineItem.id }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ typealias TimelineMediaPreviewViewModelType = StateStoreViewModel<TimelineMediaP

class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
private let timelineViewModel: TimelineViewModelProtocol
private let currentItemIDHandler: ((TimelineItemIdentifier?) -> Void)?
private let mediaProvider: MediaProviderProtocol
private let photoLibraryManager: PhotoLibraryManagerProtocol
private let userIndicatorController: UserIndicatorControllerProtocol
Expand All @@ -28,14 +29,18 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
userIndicatorController: UserIndicatorControllerProtocol,
appMediator: AppMediatorProtocol) {
timelineViewModel = context.viewModel
currentItemIDHandler = context.itemIDHandler
self.mediaProvider = mediaProvider
self.photoLibraryManager = photoLibraryManager
self.userIndicatorController = userIndicatorController
self.appMediator = appMediator

let currentItem = TimelineMediaPreviewItem(timelineItem: context.item)
let previewItems = timelineViewModel.context.viewState.timelineState.itemViewStates.compactMap(TimelineMediaPreviewItem.init)
let initialItemIndex = previewItems.firstIndex { $0.id == context.item.id } ?? 0
let currentItem = previewItems[initialItemIndex]

super.init(initialViewState: TimelineMediaPreviewViewState(previewItems: [currentItem],
super.init(initialViewState: TimelineMediaPreviewViewState(previewItems: previewItems,
initialItemIndex: initialItemIndex,
currentItem: currentItem,
transitionNamespace: context.namespace),
mediaProvider: mediaProvider)
Expand Down Expand Up @@ -76,6 +81,7 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {

private func updateCurrentItem(_ previewItem: TimelineMediaPreviewItem) async {
state.currentItem = previewItem
currentItemIDHandler?(previewItem.id)
rebuildCurrentItemActions()

if previewItem.fileHandle == nil, let source = previewItem.mediaSource {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import SwiftUI

struct TimelineMediaPreviewScreen: View {
@ObservedObject var context: TimelineMediaPreviewViewModel.Context
var onDisappear: (() -> Void)?
var itemIDHandler: ((TimelineItemIdentifier?) -> Void)?

@State private var isFullScreen = false
private var toolbarVisibility: Visibility { isFullScreen ? .hidden : .visible }
Expand All @@ -38,7 +38,7 @@ struct TimelineMediaPreviewScreen: View {
.alert(item: $context.alertInfo)
.preferredColorScheme(.dark)
.onDisappear {
onDisappear?()
itemIDHandler?(nil)
}
.zoomTransition(sourceID: currentItem.id, in: context.viewState.transitionNamespace)
}
Expand Down Expand Up @@ -148,8 +148,10 @@ private struct QuickLookView: UIViewControllerRepresentable {
let viewModelContext: TimelineMediaPreviewViewModel.Context

func makeUIViewController(context: Context) -> PreviewController {
PreviewController(coordinator: context.coordinator,
fileLoadedPublisher: viewModelContext.viewState.fileLoadedPublisher.eraseToAnyPublisher())
let fileLoadedPublisher = viewModelContext.viewState.fileLoadedPublisher.eraseToAnyPublisher()
let controller = PreviewController(coordinator: context.coordinator, fileLoadedPublisher: fileLoadedPublisher)
controller.currentPreviewItemIndex = viewModelContext.viewState.initialItemIndex
return controller
}

func updateUIViewController(_ uiViewController: PreviewController, context: Context) { }
Expand Down Expand Up @@ -183,13 +185,9 @@ private struct QuickLookView: UIViewControllerRepresentable {
// MARK: UIKit

class PreviewController: QLPreviewController {
let coordinator: Coordinator

private var cancellables: Set<AnyCancellable> = []

init(coordinator: Coordinator, fileLoadedPublisher: AnyPublisher<TimelineItemIdentifier, Never>) {
self.coordinator = coordinator

super.init(nibName: nil, bundle: nil)

dataSource = coordinator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType

actionsSubject.send(.viewItem(.init(item: item,
viewModel: activeTimelineViewModel,
namespace: namespace) { [weak self] in
self?.state.currentPreviewItemID = nil
namespace: namespace) { [weak self] itemID in
self?.state.currentPreviewItemID = itemID
}))

// Set the current item in the next run loop so that (hopefully) the presentation will be ready before we flip the thumbnail.
Expand Down

0 comments on commit 577c491

Please sign in to comment.