mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Add a fullscreen button to TimelineMediaPreviewScreen and hook up swiping through the timeline. (#3638)
* Add a fullscreen button to media previews - Not ideal but the gestures conflict with the preview controller. * Don't un-flip the preview thumbnail until the preview has disappeared, and only do it on iOS 18. * Add all of the loaded items for previewing in the preview controller.
This commit is contained in:
+4
-1
@@ -180,8 +180,11 @@ struct TimelineMediaPreviewDetailsView_Previews: PreviewProvider, TestablePrevie
|
||||
contentType: contentType))
|
||||
|
||||
let timelineKind = TimelineKind.media(isPresentedOnRoomScreen ? .roomScreen : .mediaFilesScreen)
|
||||
let timelineController = MockRoomTimelineController(timelineKind: timelineKind)
|
||||
timelineController.timelineItems = [item]
|
||||
return TimelineMediaPreviewViewModel(context: .init(item: item,
|
||||
viewModel: TimelineViewModel.mock(timelineKind: timelineKind),
|
||||
viewModel: TimelineViewModel.mock(timelineKind: timelineKind,
|
||||
timelineController: timelineController),
|
||||
namespace: previewNamespace),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
photoLibraryManager: PhotoLibraryManagerMock(.init()),
|
||||
|
||||
+4
-1
@@ -138,8 +138,11 @@ struct TimelineMediaPreviewRedactConfirmationView_Previews: PreviewProvider, Tes
|
||||
thumbnailInfo: .mockThumbnail,
|
||||
contentType: contentType))
|
||||
|
||||
let timelineController = MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen))
|
||||
timelineController.timelineItems = [item]
|
||||
return TimelineMediaPreviewViewModel(context: .init(item: item,
|
||||
viewModel: TimelineViewModel.mock,
|
||||
viewModel: TimelineViewModel.mock(timelineKind: timelineController.timelineKind,
|
||||
timelineController: timelineController),
|
||||
namespace: previewNamespace),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
photoLibraryManager: PhotoLibraryManagerMock(.init()),
|
||||
|
||||
@@ -12,18 +12,16 @@ import SwiftUI
|
||||
|
||||
struct TimelineMediaPreviewScreen: View {
|
||||
@ObservedObject var context: TimelineMediaPreviewViewModel.Context
|
||||
var itemIDHandler: ((TimelineItemIdentifier?) -> Void)?
|
||||
|
||||
@State private var isFullScreen = false
|
||||
private var toolbarVisibility: Visibility { isFullScreen ? .hidden : .visible }
|
||||
|
||||
private var currentItem: TimelineMediaPreviewItem { context.viewState.currentItem }
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Color.clear
|
||||
.overlay { QuickLookView(viewModelContext: context).ignoresSafeArea() } // Overlay to stop QL hijacking the toolbar.
|
||||
.toolbar { toolbar }
|
||||
.toolbarBackground(.visible, for: .navigationBar) // The toolbar's scrollEdgeAppearance isn't aware of the quicklook view 🤷♂️
|
||||
.toolbarBackground(.visible, for: .bottomBar)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.safeAreaInset(edge: .bottom, spacing: 0) { caption }
|
||||
quickLookPreview
|
||||
}
|
||||
.introspect(.navigationStack, on: .supportedVersions) {
|
||||
// Fixes a bug where the QuickLook view overrides the .toolbarBackground(.visible) after it loads the real item.
|
||||
@@ -39,12 +37,41 @@ struct TimelineMediaPreviewScreen: View {
|
||||
}
|
||||
.alert(item: $context.alertInfo)
|
||||
.preferredColorScheme(.dark)
|
||||
.onDisappear {
|
||||
itemIDHandler?(nil)
|
||||
}
|
||||
.zoomTransition(sourceID: currentItem.id, in: context.viewState.transitionNamespace)
|
||||
}
|
||||
|
||||
var quickLookPreview: some View {
|
||||
Color.clear // A completely clear view breaks any SwiftUI gestures (such as drag to dismiss).
|
||||
.background { QuickLookView(viewModelContext: context).ignoresSafeArea() } // Not the root view to stop QL hijacking the toolbar.
|
||||
.overlay(alignment: .topTrailing) { fullScreenButton }
|
||||
.toolbar { toolbar }
|
||||
.toolbar(toolbarVisibility, for: .navigationBar)
|
||||
.toolbar(toolbarVisibility, for: .bottomBar)
|
||||
.toolbarBackground(.visible, for: .navigationBar) // The toolbar's scrollEdgeAppearance isn't aware of the quicklook view 🤷♂️
|
||||
.toolbarBackground(.visible, for: .bottomBar)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.safeAreaInset(edge: .bottom, spacing: 0) { caption }
|
||||
}
|
||||
|
||||
private var fullScreenButton: some View {
|
||||
Button {
|
||||
withAnimation { isFullScreen.toggle() }
|
||||
} label: {
|
||||
CompoundIcon(isFullScreen ? \.collapse : \.expand, size: .xSmall, relativeTo: .compound.bodyLG)
|
||||
.padding(6)
|
||||
.background(.thinMaterial, in: Circle())
|
||||
}
|
||||
.tint(.compound.textActionPrimary)
|
||||
.padding(.top, 12)
|
||||
.padding(.trailing, 14)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var caption: some View {
|
||||
if let caption = currentItem.caption {
|
||||
if let caption = currentItem.caption, !isFullScreen {
|
||||
Text(caption)
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
@@ -55,6 +82,7 @@ struct TimelineMediaPreviewScreen: View {
|
||||
.background {
|
||||
BlurEffectView(style: .systemChromeMaterial) // Darkest material available, matches the bottom bar when content is beneath.
|
||||
}
|
||||
.transition(.move(edge: .bottom).combined(with: .opacity))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,12 +142,16 @@ struct TimelineMediaPreviewScreen: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - QuickLook
|
||||
|
||||
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) { }
|
||||
@@ -128,6 +160,8 @@ private struct QuickLookView: UIViewControllerRepresentable {
|
||||
Coordinator(viewModelContext: viewModelContext)
|
||||
}
|
||||
|
||||
// MARK: Coordinator
|
||||
|
||||
class Coordinator: NSObject, QLPreviewControllerDataSource, QLPreviewControllerDelegate {
|
||||
private let viewModelContext: TimelineMediaPreviewViewModel.Context
|
||||
|
||||
@@ -148,14 +182,12 @@ 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
|
||||
@@ -208,8 +240,12 @@ struct TimelineMediaPreviewScreen_Previews: PreviewProvider {
|
||||
thumbnailSource: nil,
|
||||
contentType: .pdf))
|
||||
|
||||
let timelineController = MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen))
|
||||
timelineController.timelineItems = [item]
|
||||
|
||||
return TimelineMediaPreviewViewModel(context: .init(item: item,
|
||||
viewModel: TimelineViewModel.mock(timelineKind: .media(.mediaFilesScreen)),
|
||||
viewModel: TimelineViewModel.mock(timelineKind: timelineController.timelineKind,
|
||||
timelineController: timelineController),
|
||||
namespace: namespace),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
photoLibraryManager: PhotoLibraryManagerMock(.init()),
|
||||
|
||||
Reference in New Issue
Block a user