// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import UIKit import QuartzCore import GRDB import DifferenceKit import SessionUIKit import SignalUtilitiesKit import SignalCoreKit public class AllMediaViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate { private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil) private var pages: [UIViewController] = [] private var targetVCIndex: Int? // MARK: - Components private lazy var tabBar: TabBar = { let result: TabBar = TabBar( tabs: [ TabBar.Tab(title: MediaStrings.media) { [weak self] in guard let self = self else { return } self.pageVC.setViewControllers([ self.pages[0] ], direction: .forward, animated: false, completion: nil) self.updateSelectButton(updatedData: self.mediaTitleViewController.viewModel.galleryData, inBatchSelectMode: self.mediaTitleViewController.isInBatchSelectMode) }, TabBar.Tab(title: MediaStrings.document) { [weak self] in guard let self = self else { return } self.pageVC.setViewControllers([ self.pages[1] ], direction: .forward, animated: false, completion: nil) self.endSelectMode() self.navigationItem.rightBarButtonItem = nil } ] ) result.themeBackgroundColor = .backgroundPrimary return result }() private var mediaTitleViewController: MediaTileViewController private var documentTitleViewController: DocumentTileViewController init(mediaTitleViewController: MediaTileViewController, documentTitleViewController: DocumentTileViewController) { self.mediaTitleViewController = mediaTitleViewController self.documentTitleViewController = documentTitleViewController super.init(nibName: nil, bundle: nil) self.mediaTitleViewController.delegate = self self.documentTitleViewController.delegate = self addChild(self.mediaTitleViewController) addChild(self.documentTitleViewController) } required init?(coder: NSCoder) { notImplemented() } // MARK: Lifecycle public override func viewDidLoad() { super.viewDidLoad() view.themeBackgroundColor = .newConversation_background // Add a custom back button if this is the only view controller if self.navigationController?.viewControllers.first == self { let backButton = UIViewController.createOWSBackButton(target: self, selector: #selector(didPressDismissButton)) self.navigationItem.leftBarButtonItem = backButton } ViewControllerUtilities.setUpDefaultSessionStyle( for: self, title: MediaStrings.allMedia, hasCustomBackButton: false ) // Set up page VC pages = [ mediaTitleViewController, documentTitleViewController ] pageVC.dataSource = self pageVC.delegate = self pageVC.setViewControllers([ mediaTitleViewController ], direction: .forward, animated: false, completion: nil) addChild(pageVC) // Set up tab bar view.addSubview(tabBar) tabBar.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.top ], to: view) // Set up page VC constraints let pageVCView = pageVC.view! view.addSubview(pageVCView) pageVCView.pin([ UIView.HorizontalEdge.leading, UIView.HorizontalEdge.trailing, UIView.VerticalEdge.bottom ], to: view) pageVCView.pin(.top, to: .bottom, of: tabBar) } // MARK: General public func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let index = pages.firstIndex(of: viewController), index != 0 else { return nil } return pages[index - 1] } public func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let index = pages.firstIndex(of: viewController), index != (pages.count - 1) else { return nil } return pages[index + 1] } // MARK: Updating public func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) { guard let targetVC = pendingViewControllers.first, let index = pages.firstIndex(of: targetVC) else { return } targetVCIndex = index } public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating isFinished: Bool, previousViewControllers: [UIViewController], transitionCompleted isCompleted: Bool) { guard isCompleted, let index = targetVCIndex else { return } tabBar.selectTab(at: index) } // MARK: Interaction @objc public func didPressDismissButton() { dismiss(animated: true, completion: nil) } // MARK: Batch Selection @objc func didTapSelect(_ sender: Any) { self.mediaTitleViewController.didTapSelect(sender) // Don't allow the user to leave mid-selection, so they realized they have // to cancel (lose) their selection if they leave. self.navigationItem.hidesBackButton = true } @objc func didCancelSelect(_ sender: Any) { endSelectMode() } func endSelectMode() { self.mediaTitleViewController.endSelectMode() self.navigationItem.hidesBackButton = false } } // MARK: - UIDocumentInteractionControllerDelegate extension AllMediaViewController: UIDocumentInteractionControllerDelegate { public func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController { return self } } // MARK: - DocumentTitleViewControllerDelegate extension AllMediaViewController: DocumentTileViewControllerDelegate { public func share(fileUrl: URL) { let shareVC = UIActivityViewController(activityItems: [ fileUrl ], applicationActivities: nil) if UIDevice.current.isIPad { shareVC.excludedActivityTypes = [] shareVC.popoverPresentationController?.permittedArrowDirections = [] shareVC.popoverPresentationController?.sourceView = self.view shareVC.popoverPresentationController?.sourceRect = self.view.bounds } navigationController?.present(shareVC, animated: true, completion: nil) } public func preview(fileUrl: URL) { let interactionController: UIDocumentInteractionController = UIDocumentInteractionController(url: fileUrl) interactionController.delegate = self interactionController.presentPreview(animated: true) } } // MARK: - DocumentTitleViewControllerDelegate extension AllMediaViewController: MediaTileViewControllerDelegate { public func presentdetailViewController(_ detailViewController: UIViewController, animated: Bool) { self.present(detailViewController, animated: animated) } public func updateSelectButton(updatedData: [MediaGalleryViewModel.SectionModel], inBatchSelectMode: Bool) { guard !updatedData.isEmpty else { self.navigationItem.rightBarButtonItem = nil return } if inBatchSelectMode { self.navigationItem.rightBarButtonItem = UIBarButtonItem( barButtonSystemItem: .cancel, target: self, action: #selector(didCancelSelect) ) } else { self.navigationItem.rightBarButtonItem = UIBarButtonItem( title: "BUTTON_SELECT".localized(), style: .plain, target: self, action: #selector(didTapSelect) ) } } } // MARK: - UIViewControllerTransitioningDelegate extension AllMediaViewController: UIViewControllerTransitioningDelegate { public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self.mediaTitleViewController.animationController(forPresented: presented, presenting: presenting, source: source) } public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return self.mediaTitleViewController.animationController(forDismissed: dismissed) } } // MARK: - MediaPresentationContextProvider extension AllMediaViewController: MediaPresentationContextProvider { func mediaPresentationContext(mediaItem: Media, in coordinateSpace: UICoordinateSpace) -> MediaPresentationContext? { return self.mediaTitleViewController.mediaPresentationContext(mediaItem: mediaItem, in: coordinateSpace) } func snapshotOverlayView(in coordinateSpace: UICoordinateSpace) -> (UIView, CGRect)? { return self.mediaTitleViewController.snapshotOverlayView(in: coordinateSpace) } }