From ccc46dfbf8c44c1d62616a2a55f6ae8514d78000 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Thu, 7 Jul 2022 16:40:13 +1000 Subject: [PATCH 01/19] =?UTF-8?q?Add=20=E2=80=9Cdocuments=E2=80=9D=20secti?= =?UTF-8?q?on=20in=20the=20all=20media=20screen=20for=20a=20conversation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Podfile.lock | 2 +- Session.xcodeproj/project.pbxproj | 8 + .../AllMediaViewController.swift | 100 +++++ .../DocumentTitleViewController.swift | 375 ++++++++++++++++++ .../MediaGalleryViewModel.swift | 67 +++- .../MediaPageViewController.swift | 2 +- .../Translations/en.lproj/Localizable.strings | 3 +- .../Utilities/CommonStrings.swift | 4 + 8 files changed, 552 insertions(+), 9 deletions(-) create mode 100644 Session/Media Viewing & Editing/AllMediaViewController.swift create mode 100644 Session/Media Viewing & Editing/DocumentTitleViewController.swift diff --git a/Podfile.lock b/Podfile.lock index 70045a1da..bab1ae426 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -232,4 +232,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 6ab902a81a379cc2c0a9a92c334c78d413190338 -COCOAPODS: 1.11.3 +COCOAPODS: 1.11.2 diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 7c1a8f752..e422c0866 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -124,6 +124,7 @@ 7B1D74AA27BCC16E0030B423 /* NSENotificationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1D74A927BCC16E0030B423 /* NSENotificationPresenter.swift */; }; 7B1D74AC27BDE7510030B423 /* Promise+Timeout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1D74AB27BDE7510030B423 /* Promise+Timeout.swift */; }; 7B1D74B027C365960030B423 /* Timer+MainThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1D74AF27C365960030B423 /* Timer+MainThread.swift */; }; + 7B46AAAF28766DF4001AF2DC /* AllMediaViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B46AAAE28766DF4001AF2DC /* AllMediaViewController.swift */; }; 7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */; }; 7B4C75CD26BB92060000AC89 /* DeletedMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */; }; 7B7CB189270430D20079FF93 /* CallMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB188270430D20079FF93 /* CallMessageView.swift */; }; @@ -146,6 +147,7 @@ 7BAF54D427ACCF01003D12F8 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */; }; 7BAF54D827ACD0E3003D12F8 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */; }; 7BAF54DC27ACD12B003D12F8 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BAF54DB27ACD12B003D12F8 /* UIColor+Extensions.swift */; }; + 7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */; }; 7BC01A3E241F40AB00BC7C55 /* NotificationServiceExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */; }; 7BC01A42241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 7BC707F227290ACB002817AD /* SessionCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC707F127290ACB002817AD /* SessionCallManager.swift */; }; @@ -1141,6 +1143,7 @@ 7B1D74AB27BDE7510030B423 /* Promise+Timeout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+Timeout.swift"; sourceTree = ""; }; 7B1D74AF27C365960030B423 /* Timer+MainThread.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Timer+MainThread.swift"; sourceTree = ""; }; 7B2DB2AD26F1B0FF0035B509 /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Localizable.strings; sourceTree = ""; }; + 7B46AAAE28766DF4001AF2DC /* AllMediaViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllMediaViewController.swift; sourceTree = ""; }; 7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsendRequest.swift; sourceTree = ""; }; 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedMessageView.swift; sourceTree = ""; }; 7B7CB188270430D20079FF93 /* CallMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallMessageView.swift; sourceTree = ""; }; @@ -1163,6 +1166,7 @@ 7BAF54D227ACCF01003D12F8 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = ""; }; 7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = ""; }; 7BAF54DB27ACD12B003D12F8 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = ""; }; + 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentTitleViewController.swift; sourceTree = ""; }; 7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SessionNotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 7BC01A3D241F40AB00BC7C55 /* NotificationServiceExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtension.swift; sourceTree = ""; }; 7BC01A3F241F40AB00BC7C55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -2855,6 +2859,7 @@ FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */, 45F32C1D205718B000A300D5 /* MediaPageViewController.swift */, 454A84032059C787008B8C75 /* MediaTileViewController.swift */, + 7BBBDC452875600700747E59 /* DocumentTitleViewController.swift */, 34E3E5671EC4B19400495BAC /* AudioProgressView.swift */, 346B66301F4E29B200E5122F /* CropScaleImageViewController.swift */, 34969559219B605E00DCFE74 /* ImagePickerController.swift */, @@ -2865,6 +2870,7 @@ 4C21D5D7223AC60F00EF8A77 /* PhotoCapture.swift */, 4C1885D1218F8E1C00B67051 /* PhotoGridViewCell.swift */, 4C4AE69F224AF21900D4AF6F /* SendMediaNavigationController.swift */, + 7B46AAAE28766DF4001AF2DC /* AllMediaViewController.swift */, ); path = "Media Viewing & Editing"; sourceTree = ""; @@ -5298,6 +5304,7 @@ FDFDE124282D04F20098B17F /* MediaDismissAnimationController.swift in Sources */, 7BA6890F27325CE300EFC32F /* SessionCallManager+CXProvider.swift in Sources */, 34BECE301F7ABCF800D7438D /* GifPickerLayout.swift in Sources */, + 7B46AAAF28766DF4001AF2DC /* AllMediaViewController.swift in Sources */, C331FFFE2558FF3B00070591 /* FullConversationCell.swift in Sources */, B8F5F72325F1B4CA003BF8D4 /* DownloadAttachmentModal.swift in Sources */, C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */, @@ -5308,6 +5315,7 @@ B835246E25C38ABF0089A44F /* ConversationVC.swift in Sources */, B8AF4BB426A5204600583500 /* SendSeedModal.swift in Sources */, B821494625D4D6FF009C0F2A /* URLModal.swift in Sources */, + 7BBBDC462875600700747E59 /* DocumentTitleViewController.swift in Sources */, B877E24226CA12910007970A /* CallVC.swift in Sources */, 7BA6890D27325CCC00EFC32F /* SessionCallManager+CXCallController.swift in Sources */, C374EEEB25DA3CA70073A857 /* ConversationTitleView.swift in Sources */, diff --git a/Session/Media Viewing & Editing/AllMediaViewController.swift b/Session/Media Viewing & Editing/AllMediaViewController.swift new file mode 100644 index 000000000..121db8b43 --- /dev/null +++ b/Session/Media Viewing & Editing/AllMediaViewController.swift @@ -0,0 +1,100 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import UIKit +import QuartzCore +import GRDB +import DifferenceKit +import SessionUIKit +import SignalUtilitiesKit + +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 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) + }, + 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) + } + ] + return TabBar(tabs: tabs) + }() + + 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) + } + + required init?(coder: NSCoder) { + notImplemented() + } + + // MARK: Lifecycle + public override func viewDidLoad() { + super.viewDidLoad() + + // Add a custom back button if this is the only view controller + if self.navigationController?.viewControllers.first == self { + let backButton = OWSViewController.createOWSBackButton(withTarget: 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) + // 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) + } +} diff --git a/Session/Media Viewing & Editing/DocumentTitleViewController.swift b/Session/Media Viewing & Editing/DocumentTitleViewController.swift new file mode 100644 index 000000000..820e2eabb --- /dev/null +++ b/Session/Media Viewing & Editing/DocumentTitleViewController.swift @@ -0,0 +1,375 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import UIKit +import QuartzCore +import GRDB +import DifferenceKit +import SessionUIKit +import SignalUtilitiesKit + +public class DocumentTileViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { + + /// This should be larger than one screen size so we don't have to call it multiple times in rapid succession, but not + /// so large that loading get's really chopping + static let itemPageSize: Int = Int(11 * itemsPerPortraitRow) + static let itemsPerPortraitRow: CGFloat = 4 + static let interItemSpacing: CGFloat = 2 + static let footerBarHeight: CGFloat = 40 + static let loadMoreHeaderHeight: CGFloat = 100 + + private let viewModel: MediaGalleryViewModel + private var hasLoadedInitialData: Bool = false + private var didFinishInitialLayout: Bool = false + private var isAutoLoadingNextPage: Bool = false + private var currentTargetOffset: CGPoint? + + // MARK: - Initialization + + init(viewModel: MediaGalleryViewModel) { + self.viewModel = viewModel + Storage.shared.addObserver(viewModel.pagedDataObserver) + + super.init(nibName: nil, bundle: nil) + } + + required public init?(coder aDecoder: NSCoder) { + notImplemented() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + // MARK: - UI + + override public var supportedInterfaceOrientations: UIInterfaceOrientationMask { + return .allButUpsideDown + } + + lazy var tableView: UITableView = { + let result = UITableView(frame: .zero, style: .grouped) + result.backgroundColor = Colors.navigationBarBackground + result.separatorStyle = .none + result.showsVerticalScrollIndicator = false + result.register(view: DocumentCell.self) + result.delegate = self + result.dataSource = self + // Feels a bit weird to have content smashed all the way to the bottom edge. + result.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 20, right: 0) + + return result + }() + + // MARK: - Lifecycle + + override public func viewDidLoad() { + super.viewDidLoad() + + // Add a custom back button if this is the only view controller + if self.navigationController?.viewControllers.first == self { + let backButton = OWSViewController.createOWSBackButton(withTarget: self, selector: #selector(didPressDismissButton)) + self.navigationItem.leftBarButtonItem = backButton + } + + ViewControllerUtilities.setUpDefaultSessionStyle( + for: self, + title: MediaStrings.document, + hasCustomBackButton: false + ) + + view.addSubview(self.tableView) + tableView.autoPin(toEdgesOf: view) + + // Notifications + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationDidBecomeActive(_:)), + name: UIApplication.didBecomeActiveNotification, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(applicationDidResignActive(_:)), + name: UIApplication.didEnterBackgroundNotification, object: nil + ) + } + + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + startObservingChanges() + } + + public override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + self.didFinishInitialLayout = true + } + + public override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + stopObservingChanges() + } + + @objc func applicationDidBecomeActive(_ notification: Notification) { + startObservingChanges() + } + + @objc func applicationDidResignActive(_ notification: Notification) { + stopObservingChanges() + } + + // MARK: - Updating + + private func performInitialScrollIfNeeded() { + // Ensure this hasn't run before and that we have data (The 'galleryData' will always + // contain something as the 'empty' state is a section within 'galleryData') + guard !self.didFinishInitialLayout && self.hasLoadedInitialData else { return } + + // If we have a focused item then we want to scroll to it + guard let focusedIndexPath: IndexPath = self.viewModel.focusedIndexPath else { return } + + Logger.debug("scrolling to focused item at indexPath: \(focusedIndexPath)") + self.view.layoutIfNeeded() + self.tableView.scrollToRow(at: focusedIndexPath, at: .middle, animated: false) + + // Now that the data has loaded we need to check if either of the "load more" sections are + // visible and trigger them if so + // + // Note: We do it this way as we want to trigger the load behaviour for the first section + // if it has one before trying to trigger the load behaviour for the last section + self.autoLoadNextPageIfNeeded() + } + + private func autoLoadNextPageIfNeeded() { + guard !self.isAutoLoadingNextPage else { return } + + self.isAutoLoadingNextPage = true + + DispatchQueue.main.asyncAfter(deadline: .now() + PagedData.autoLoadNextPageDelay) { [weak self] in + self?.isAutoLoadingNextPage = false + + // Note: We sort the headers as we want to prioritise loading newer pages over older ones + let sortedVisibleIndexPaths: [IndexPath] = (self?.tableView.indexPathsForVisibleRows ?? []).sorted() + + for headerIndexPath in sortedVisibleIndexPaths { + let section: MediaGalleryViewModel.SectionModel? = self?.viewModel.galleryData[safe: headerIndexPath.section] + + switch section?.model { + case .loadNewer, .loadOlder: + // Attachments are loaded in descending order so 'loadOlder' actually corresponds with + // 'pageAfter' in this case + self?.viewModel.pagedDataObserver?.load(section?.model == .loadOlder ? + .pageAfter : + .pageBefore + ) + return + + default: continue + } + } + } + } + + private func startObservingChanges() { + // Start observing for data changes (will callback on the main thread) + self.viewModel.onGalleryChange = { [weak self] updatedGalleryData in + self?.handleUpdates(updatedGalleryData) + } + } + + private func stopObservingChanges() { + // Note: The 'pagedDataObserver' will continue to get changes but + // we don't want to trigger any UI updates + self.viewModel.onGalleryChange = nil + } + + private func handleUpdates(_ updatedGalleryData: [MediaGalleryViewModel.SectionModel]) { + // Ensure the first load runs without animations (if we don't do this the cells will animate + // in from a frame of CGRect.zero) + guard hasLoadedInitialData else { + self.hasLoadedInitialData = true + self.viewModel.updateGalleryData(updatedGalleryData) + + UIView.performWithoutAnimation { + self.tableView.reloadData() + self.performInitialScrollIfNeeded() + } + return + } + + + } + + // MARK: - Interactions + + @objc public func didPressDismissButton() { + let presentedNavController: UINavigationController? = (self.presentingViewController as? UINavigationController) + let mediaPageViewController: MediaPageViewController? = ( + (presentedNavController?.viewControllers.last as? MediaPageViewController) ?? + (self.presentingViewController as? MediaPageViewController) + ) + + // If the album was presented from a 'MediaPageViewController' and it has no more data (ie. + // all album items had been deleted) then dismiss to the screen before that one + guard mediaPageViewController?.viewModel.albumData.isEmpty != true else { + presentedNavController?.presentingViewController?.dismiss(animated: true, completion: nil) + return + } + + dismiss(animated: true, completion: nil) + } + + // MARK: - UITableViewDataSource + + public func numberOfSections(in tableView: UITableView) -> Int { + print("numberOfSections: \(self.viewModel.galleryData.count)") + return self.viewModel.galleryData.count + } + + public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + print("rows: \(self.viewModel.galleryData[section])") + return self.viewModel.galleryData[section].elements.count + } + + public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell: DocumentCell = tableView.dequeue(type: DocumentCell.self, for: indexPath) + cell.update(with: self.viewModel.galleryData[indexPath.section].elements[indexPath.row]) + return cell + } + + // MARK: - UITableViewDelegate + + public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let section: MediaGalleryViewModel.SectionModel = self.viewModel.galleryData[section] + + switch section.model { + case .emptyGallery, .loadOlder, .loadNewer: + return nil + + case .galleryMonth(let date): + let headerView: UIView = UIView() + let label = UILabel() + label.textColor = Colors.text + label.text = date.localizedString + + let blurEffect = UIBlurEffect(style: .dark) + let blurEffectView = UIVisualEffectView(effect: blurEffect) + + blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + + headerView.backgroundColor = isLightMode ? Colors.cellBackground : UIColor.ows_black.withAlphaComponent(OWSNavigationBar.backgroundBlurMutingFactor) + + headerView.addSubview(blurEffectView) + headerView.addSubview(label) + + blurEffectView.autoPinEdgesToSuperviewEdges() + blurEffectView.isHidden = isLightMode + label.autoPinEdge(toSuperviewMargin: .trailing) + label.autoPinEdge(toSuperviewMargin: .leading) + label.autoVCenterInSuperview() + + return headerView + } + } + + public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return 50 + } +} + +class DocumentCell: UITableViewCell { + + // MARK: - Initialization + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + setUpViewHierarchy() + setupLayout() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + + setUpViewHierarchy() + setupLayout() + } + + // MARK: - UI + + private static let iconImageViewSize: CGSize = CGSize(width: 31, height: 40) + + private let iconImageView: UIImageView = { + let result: UIImageView = UIImageView(image: #imageLiteral(resourceName: "File").withRenderingMode(.alwaysTemplate)) + result.translatesAutoresizingMaskIntoConstraints = false + result.tintColor = Colors.text + + return result + }() + + private let titleLabel: UILabel = { + let result: UILabel = UILabel() + result.translatesAutoresizingMaskIntoConstraints = false + result.setContentHuggingPriority(.defaultHigh, for: .horizontal) + result.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + result.font = .boldSystemFont(ofSize: Values.smallFontSize) + result.textColor = Colors.text + result.lineBreakMode = .byTruncatingTail + + return result + }() + + private let detailLabel: UILabel = { + let result: UILabel = UILabel() + result.translatesAutoresizingMaskIntoConstraints = false + result.setContentHuggingPriority(.defaultHigh, for: .horizontal) + result.setContentCompressionResistancePriority(.defaultLow, for: .horizontal) + result.font = .systemFont(ofSize: Values.smallFontSize) + result.textColor = Colors.text + result.lineBreakMode = .byTruncatingTail + + return result + }() + + private func setUpViewHierarchy() { + backgroundColor = Colors.cellBackground + selectedBackgroundView = UIView() + selectedBackgroundView?.backgroundColor = Colors.cellSelected + + + contentView.addSubview(iconImageView) + contentView.addSubview(titleLabel) + contentView.addSubview(detailLabel) + } + + // MARK: - Layout + + private func setupLayout() { + NSLayoutConstraint.activate([ + contentView.heightAnchor.constraint(equalToConstant: 68), + + iconImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: Values.mediumSpacing), + iconImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor), + iconImageView.widthAnchor.constraint(equalToConstant: Self.iconImageViewSize.width), + iconImageView.heightAnchor.constraint(equalToConstant: Self.iconImageViewSize.height), + + titleLabel.leftAnchor.constraint(equalTo: iconImageView.rightAnchor, constant: Values.mediumSpacing), + titleLabel.rightAnchor.constraint(lessThanOrEqualTo: contentView.rightAnchor, constant: -Values.mediumSpacing), + titleLabel.topAnchor.constraint(equalTo: iconImageView.topAnchor), + + detailLabel.leftAnchor.constraint(equalTo: iconImageView.rightAnchor, constant: Values.mediumSpacing), + detailLabel.rightAnchor.constraint(lessThanOrEqualTo: contentView.rightAnchor, constant: -Values.mediumSpacing), + detailLabel.bottomAnchor.constraint(equalTo: iconImageView.bottomAnchor), + ]) + } + + // MARK: - Content + + func update(with item: MediaGalleryViewModel.Item) { + let attachment = item.attachment + titleLabel.text = attachment.sourceFilename ?? "File" + detailLabel.text = "\(OWSFormat.formatFileSize(UInt(attachment.byteCount)))" + } +} diff --git a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift index cc425d2d3..21dc80624 100644 --- a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift +++ b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift @@ -18,12 +18,19 @@ public class MediaGalleryViewModel { case loadNewer } + // MARK: Media type + public enum MediaType { + case media + case document + } + // MARK: - Variables public let threadId: String public let threadVariant: SessionThread.Variant private var focusedAttachmentId: String? public private(set) var focusedIndexPath: IndexPath? + public var mediaType: MediaType /// This value is the current state of an album view private var cachedInteractionIdBefore: Atomic<[Int64: Int64]> = Atomic([:]) @@ -54,6 +61,7 @@ public class MediaGalleryViewModel { threadId: String, threadVariant: SessionThread.Variant, isPagedData: Bool, + mediaType: MediaType, pageSize: Int = 1, focusedAttachmentId: String? = nil, performInitialQuerySync: Bool = false @@ -62,6 +70,7 @@ public class MediaGalleryViewModel { self.threadVariant = threadVariant self.focusedAttachmentId = focusedAttachmentId self.pagedDataObserver = nil + self.mediaType = mediaType guard isPagedData else { return } @@ -80,7 +89,7 @@ public class MediaGalleryViewModel { ) ], joinSQL: Item.joinSQL, - filterSQL: Item.filterSQL(threadId: threadId), + filterSQL: Item.filterSQL(threadId: threadId, mediaType: self.mediaType), orderSQL: Item.galleryOrderSQL, dataQuery: Item.baseQuery(orderSQL: Item.galleryOrderSQL), onChangeUnsorted: { [weak self] updatedData, updatedPageInfo in @@ -243,12 +252,12 @@ public class MediaGalleryViewModel { """ }() - fileprivate static func filterSQL(threadId: String) -> SQL { + fileprivate static func filterSQL(threadId: String, mediaType: MediaType) -> SQL { let interaction: TypedTableAlias = TypedTableAlias() let attachment: TypedTableAlias = TypedTableAlias() return SQL(""" - \(attachment[.isVisualMedia]) = true AND + \(attachment[.isVisualMedia]) = \(mediaType == .media ? true : false) AND \(attachment[.isValid]) = true AND \(interaction[.threadId]) = \(threadId) """) @@ -503,7 +512,8 @@ public class MediaGalleryViewModel { let viewModel: MediaGalleryViewModel = MediaGalleryViewModel( threadId: threadId, threadVariant: threadVariant, - isPagedData: false + isPagedData: false, + mediaType: .media ) viewModel.loadAndCacheAlbumData(for: interactionId) viewModel.replaceAlbumObservation(toObservationFor: interactionId) @@ -528,7 +538,7 @@ public class MediaGalleryViewModel { return navController } - public static func createTileViewController( + public static func createMediaTileViewController( threadId: String, threadVariant: SessionThread.Variant, focusedAttachmentId: String?, @@ -538,6 +548,7 @@ public class MediaGalleryViewModel { threadId: threadId, threadVariant: threadVariant, isPagedData: true, + mediaType: .media, pageSize: MediaTileViewController.itemPageSize, focusedAttachmentId: focusedAttachmentId, performInitialQuerySync: performInitialQuerySync @@ -547,6 +558,50 @@ public class MediaGalleryViewModel { viewModel: viewModel ) } + + public static func createDocumentTitleViewController( + threadId: String, + threadVariant: SessionThread.Variant, + focusedAttachmentId: String?, + performInitialQuerySync: Bool = false + ) -> DocumentTileViewController { + let viewModel: MediaGalleryViewModel = MediaGalleryViewModel( + threadId: threadId, + threadVariant: threadVariant, + isPagedData: true, + mediaType: .document, + pageSize: MediaTileViewController.itemPageSize, + focusedAttachmentId: focusedAttachmentId, + performInitialQuerySync: performInitialQuerySync + ) + + return DocumentTileViewController( + viewModel: viewModel + ) + } + + public static func createAllMediaViewController( + threadId: String, + threadVariant: SessionThread.Variant, + focusedAttachmentId: String?, + performInitialQuerySync: Bool = false + ) -> AllMediaViewController { + let mediaTitleViewController = createMediaTileViewController( + threadId: threadId, + threadVariant: threadVariant, + focusedAttachmentId: focusedAttachmentId, + performInitialQuerySync: performInitialQuerySync) + + let documentTitleViewController = createDocumentTitleViewController( + threadId: threadId, + threadVariant: threadVariant, + focusedAttachmentId: focusedAttachmentId, + performInitialQuerySync: performInitialQuerySync) + + return AllMediaViewController( + mediaTitleViewController: mediaTitleViewController, + documentTitleViewController: documentTitleViewController) + } } // MARK: - Objective-C Support @@ -558,7 +613,7 @@ public class SNMediaGallery: NSObject { @objc(pushTileViewWithSliderEnabledForThreadId:isClosedGroup:isOpenGroup:fromNavController:) static func pushTileView(threadId: String, isClosedGroup: Bool, isOpenGroup: Bool, fromNavController: OWSNavigationController) { fromNavController.pushViewController( - MediaGalleryViewModel.createTileViewController( + MediaGalleryViewModel.createAllMediaViewController( threadId: threadId, threadVariant: { if isClosedGroup { return .closedGroup } diff --git a/Session/Media Viewing & Editing/MediaPageViewController.swift b/Session/Media Viewing & Editing/MediaPageViewController.swift index 7b9f96349..53ecb371b 100644 --- a/Session/Media Viewing & Editing/MediaPageViewController.swift +++ b/Session/Media Viewing & Editing/MediaPageViewController.swift @@ -471,7 +471,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou // Otherwise if we came via the conversation screen we need to push a new // instance of MediaTileViewController - let tileViewController: MediaTileViewController = MediaGalleryViewModel.createTileViewController( + let tileViewController: MediaTileViewController = MediaGalleryViewModel.createMediaTileViewController( threadId: self.viewModel.threadId, threadVariant: self.viewModel.threadVariant, focusedAttachmentId: currentItem.attachment.id, diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index 57ef01ba2..499183d79 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -660,4 +660,5 @@ "MESSAGE_TRIMMING_TITLE" = "Message Trimming"; "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; "MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; - +"MEDIA_TAB_TITLE" = "Media"; +"DOCUMENT_TAB_TITLE" = "Document"; diff --git a/SignalUtilitiesKit/Utilities/CommonStrings.swift b/SignalUtilitiesKit/Utilities/CommonStrings.swift index 0bcac0c4f..37dad1418 100644 --- a/SignalUtilitiesKit/Utilities/CommonStrings.swift +++ b/SignalUtilitiesKit/Utilities/CommonStrings.swift @@ -86,6 +86,10 @@ public class NotificationStrings: NSObject { @objc public class MediaStrings: NSObject { @objc static public let allMedia = NSLocalizedString("MEDIA_DETAIL_VIEW_ALL_MEDIA_BUTTON", comment: "nav bar button item") + @objc + static public let media = NSLocalizedString("MEDIA_TAB_TITLE", comment: "media tab title") + @objc + static public let document = NSLocalizedString("DOCUMENT_TAB_TITLE", comment: "document tab title") } @objc public class SafetyNumberStrings: NSObject { From 0d25fb1c48544d3f11efcc987e11722183ed6622 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Thu, 7 Jul 2022 16:57:21 +1000 Subject: [PATCH 02/19] wip: add headers for different status --- .../DocumentTitleViewController.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Session/Media Viewing & Editing/DocumentTitleViewController.swift b/Session/Media Viewing & Editing/DocumentTitleViewController.swift index 820e2eabb..08b544d21 100644 --- a/Session/Media Viewing & Editing/DocumentTitleViewController.swift +++ b/Session/Media Viewing & Editing/DocumentTitleViewController.swift @@ -275,7 +275,15 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, } public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return 50 + let section: MediaGalleryViewModel.SectionModel = self.viewModel.galleryData[section] + + switch section.model { + case .emptyGallery, .loadOlder, .loadNewer: + return MediaTileViewController.loadMoreHeaderHeight + + case .galleryMonth: + return 50 + } } } From 87a9bf9bce0e00a3bedc18bea6f9a3a46de0d48f Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Fri, 8 Jul 2022 14:40:48 +1000 Subject: [PATCH 03/19] feat: add view for empty and loading state of document view --- .../DocumentTitleViewController.swift | 98 ++++++++++++++----- .../Translations/en.lproj/Localizable.strings | 3 + 2 files changed, 79 insertions(+), 22 deletions(-) diff --git a/Session/Media Viewing & Editing/DocumentTitleViewController.swift b/Session/Media Viewing & Editing/DocumentTitleViewController.swift index 08b544d21..5c8a8e6cf 100644 --- a/Session/Media Viewing & Editing/DocumentTitleViewController.swift +++ b/Session/Media Viewing & Editing/DocumentTitleViewController.swift @@ -246,30 +246,22 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, switch section.model { case .emptyGallery, .loadOlder, .loadNewer: - return nil + let headerView: DocumentStaticHeaderView = DocumentStaticHeaderView() + headerView.configure( + title: { + switch section.model { + case .emptyGallery: return "DOCUMENT_TILES_EMPTY_DOCUMENT".localized() + case .loadOlder: return "DOCUMENT_TILES_LOADING_OLDER_LABEL".localized() + case .loadNewer: return "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL".localized() + case .galleryMonth: return "" // Impossible case + } + }() + ) + return headerView case .galleryMonth(let date): - let headerView: UIView = UIView() - let label = UILabel() - label.textColor = Colors.text - label.text = date.localizedString - - let blurEffect = UIBlurEffect(style: .dark) - let blurEffectView = UIVisualEffectView(effect: blurEffect) - - blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] - - headerView.backgroundColor = isLightMode ? Colors.cellBackground : UIColor.ows_black.withAlphaComponent(OWSNavigationBar.backgroundBlurMutingFactor) - - headerView.addSubview(blurEffectView) - headerView.addSubview(label) - - blurEffectView.autoPinEdgesToSuperviewEdges() - blurEffectView.isHidden = isLightMode - label.autoPinEdge(toSuperviewMargin: .trailing) - label.autoPinEdge(toSuperviewMargin: .leading) - label.autoVCenterInSuperview() - + let headerView: DocumentSectionHeaderView = DocumentSectionHeaderView() + headerView.configure(title: date.localizedString) return headerView } } @@ -381,3 +373,65 @@ class DocumentCell: UITableViewCell { detailLabel.text = "\(OWSFormat.formatFileSize(UInt(attachment.byteCount)))" } } + +class DocumentSectionHeaderView: UIView { + + let label: UILabel + + override init(frame: CGRect) { + label = UILabel() + label.textColor = Colors.text + + let blurEffect = UIBlurEffect(style: .dark) + let blurEffectView = UIVisualEffectView(effect: blurEffect) + + blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + + super.init(frame: frame) + + self.backgroundColor = isLightMode ? Colors.cellBackground : UIColor.ows_black.withAlphaComponent(OWSNavigationBar.backgroundBlurMutingFactor) + + self.addSubview(blurEffectView) + self.addSubview(label) + + blurEffectView.autoPinEdgesToSuperviewEdges() + blurEffectView.isHidden = isLightMode + label.autoPinEdge(toSuperviewMargin: .trailing) + label.autoPinEdge(toSuperviewMargin: .leading) + label.autoVCenterInSuperview() + } + + @available(*, unavailable, message: "Unimplemented") + required init?(coder aDecoder: NSCoder) { + notImplemented() + } + + public func configure(title: String) { + self.label.text = title + } +} + +class DocumentStaticHeaderView: UIView { + + let label = UILabel() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(label) + + label.textColor = Colors.text + label.textAlignment = .center + label.numberOfLines = 0 + label.autoPinEdgesToSuperviewMargins(with: UIEdgeInsets(top: 0, leading: Values.largeSpacing, bottom: 0, trailing: Values.largeSpacing)) + } + + @available(*, unavailable, message: "Unimplemented") + required public init?(coder aDecoder: NSCoder) { + notImplemented() + } + + public func configure(title: String) { + self.label.text = title + } +} diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index 499183d79..5fb9780e8 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -662,3 +662,6 @@ "MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; "MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Document"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; From 5b285e6bef4068a8a91bb471bdc4fd20e40bbe0d Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Fri, 8 Jul 2022 14:41:20 +1000 Subject: [PATCH 04/19] fix: filter audio message attachments out of documents --- .../MediaGalleryViewModel.swift | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift index 21dc80624..9a454597d 100644 --- a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift +++ b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift @@ -256,11 +256,21 @@ public class MediaGalleryViewModel { let interaction: TypedTableAlias = TypedTableAlias() let attachment: TypedTableAlias = TypedTableAlias() - return SQL(""" - \(attachment[.isVisualMedia]) = \(mediaType == .media ? true : false) AND - \(attachment[.isValid]) = true AND - \(interaction[.threadId]) = \(threadId) - """) + switch (mediaType) { + case .media: + return SQL(""" + \(attachment[.isVisualMedia]) = true AND + \(attachment[.isValid]) = true AND + \(interaction[.threadId]) = \(threadId) + """) + case .document: + return SQL(""" + \(attachment[.isVisualMedia]) = false AND + \(attachment[.isValid]) = true AND + \(attachment[.variant]) = \(Attachment.Variant.standard) AND + \(interaction[.threadId]) = \(threadId) AND + """) + } } fileprivate static let galleryOrderSQL: SQL = { From 3241d66905bfeb4ffbcf12755a8e91ce165b5138 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Mon, 11 Jul 2022 10:29:19 +1000 Subject: [PATCH 05/19] clean --- .../Media Viewing & Editing/DocumentTitleViewController.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Session/Media Viewing & Editing/DocumentTitleViewController.swift b/Session/Media Viewing & Editing/DocumentTitleViewController.swift index 5c8a8e6cf..7c59d63d5 100644 --- a/Session/Media Viewing & Editing/DocumentTitleViewController.swift +++ b/Session/Media Viewing & Editing/DocumentTitleViewController.swift @@ -198,8 +198,6 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, } return } - - } // MARK: - Interactions @@ -224,12 +222,10 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, // MARK: - UITableViewDataSource public func numberOfSections(in tableView: UITableView) -> Int { - print("numberOfSections: \(self.viewModel.galleryData.count)") return self.viewModel.galleryData.count } public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - print("rows: \(self.viewModel.galleryData[section])") return self.viewModel.galleryData[section].elements.count } From 8d385de389cc42d8e2d40bcd81d837a97cb7f1e3 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Mon, 11 Jul 2022 11:31:42 +1000 Subject: [PATCH 06/19] clean --- .../DocumentTitleViewController.swift | 24 +++++++++---------- .../MediaGalleryViewModel.swift | 1 + 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Session/Media Viewing & Editing/DocumentTitleViewController.swift b/Session/Media Viewing & Editing/DocumentTitleViewController.swift index 7c59d63d5..21227646b 100644 --- a/Session/Media Viewing & Editing/DocumentTitleViewController.swift +++ b/Session/Media Viewing & Editing/DocumentTitleViewController.swift @@ -242,18 +242,18 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, switch section.model { case .emptyGallery, .loadOlder, .loadNewer: - let headerView: DocumentStaticHeaderView = DocumentStaticHeaderView() - headerView.configure( - title: { - switch section.model { - case .emptyGallery: return "DOCUMENT_TILES_EMPTY_DOCUMENT".localized() - case .loadOlder: return "DOCUMENT_TILES_LOADING_OLDER_LABEL".localized() - case .loadNewer: return "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL".localized() - case .galleryMonth: return "" // Impossible case - } - }() - ) - return headerView + let headerView: DocumentStaticHeaderView = DocumentStaticHeaderView() + headerView.configure( + title: { + switch section.model { + case .emptyGallery: return "DOCUMENT_TILES_EMPTY_DOCUMENT".localized() + case .loadOlder: return "DOCUMENT_TILES_LOADING_OLDER_LABEL".localized() + case .loadNewer: return "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL".localized() + case .galleryMonth: return "" // Impossible case + } + }() + ) + return headerView case .galleryMonth(let date): let headerView: DocumentSectionHeaderView = DocumentSectionHeaderView() diff --git a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift index 52f183451..9a454597d 100644 --- a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift +++ b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift @@ -267,6 +267,7 @@ public class MediaGalleryViewModel { return SQL(""" \(attachment[.isVisualMedia]) = false AND \(attachment[.isValid]) = true AND + \(attachment[.variant]) = \(Attachment.Variant.standard) AND \(interaction[.threadId]) = \(threadId) AND """) } From 3f9e26729c5b6dee131d71f308951a2d0daee732 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Mon, 11 Jul 2022 14:26:14 +1000 Subject: [PATCH 07/19] fix sql statement --- Session/Media Viewing & Editing/MediaGalleryViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift index 9a454597d..6c7550bf1 100644 --- a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift +++ b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift @@ -268,7 +268,7 @@ public class MediaGalleryViewModel { \(attachment[.isVisualMedia]) = false AND \(attachment[.isValid]) = true AND \(attachment[.variant]) = \(Attachment.Variant.standard) AND - \(interaction[.threadId]) = \(threadId) AND + \(interaction[.threadId]) = \(threadId) """) } } From 533edd903b5aeadb40245d2de2d88b60a6c036b6 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Mon, 11 Jul 2022 15:03:57 +1000 Subject: [PATCH 08/19] feat: implement handle update document view --- .../DocumentTitleViewController.swift | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Session/Media Viewing & Editing/DocumentTitleViewController.swift b/Session/Media Viewing & Editing/DocumentTitleViewController.swift index 21227646b..9e937ab4b 100644 --- a/Session/Media Viewing & Editing/DocumentTitleViewController.swift +++ b/Session/Media Viewing & Editing/DocumentTitleViewController.swift @@ -198,6 +198,43 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, } return } + + let isInsertingAtTop: Bool = { + let oldFirstSectionIsLoadMore: Bool = ( + self.viewModel.galleryData.first?.model == .loadNewer || + self.viewModel.galleryData.first?.model == .loadOlder + ) + let oldTargetSectionIndex: Int = (oldFirstSectionIsLoadMore ? 1 : 0) + + guard + let newTargetSectionIndex = updatedGalleryData + .firstIndex(where: { $0.model == self.viewModel.galleryData[safe: oldTargetSectionIndex]?.model }), + let oldFirstItem: MediaGalleryViewModel.Item = self.viewModel.galleryData[safe: oldTargetSectionIndex]?.elements.first, + let newFirstItemIndex = updatedGalleryData[safe: newTargetSectionIndex]?.elements.firstIndex(of: oldFirstItem) + else { return false } + + return (newTargetSectionIndex > oldTargetSectionIndex || newFirstItemIndex > 0) + }() + + CATransaction.begin() + + if isInsertingAtTop { CATransaction.setDisableActions(true) } + + self.tableView.reload( + using: StagedChangeset(source: self.viewModel.galleryData, target: updatedGalleryData), + with: .automatic, + interrupt: { $0.changeCount > MediaTileViewController.itemPageSize } + ) { [weak self] updatedData in + self?.viewModel.updateGalleryData(updatedData) + } + + CATransaction.setCompletionBlock { [weak self] in + // If one of the "load more" sections is still visible once the animation completes then + // trigger another "load more" (after a small delay to minimize animation bugginess) + self?.autoLoadNextPageIfNeeded() + } + CATransaction.commit() + } // MARK: - Interactions @@ -273,6 +310,10 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, return 50 } } + + public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: false) + } } class DocumentCell: UITableViewCell { From d9f05f5b9c3b5c8675b42ff9f0f8512ff5d484c9 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Tue, 12 Jul 2022 15:29:13 +1000 Subject: [PATCH 09/19] fix: filter voice messages with file name to prevent some cases that the attachment is not marked as voice message --- Session/Media Viewing & Editing/MediaGalleryViewModel.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift index 6c7550bf1..41d7130db 100644 --- a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift +++ b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift @@ -268,6 +268,7 @@ public class MediaGalleryViewModel { \(attachment[.isVisualMedia]) = false AND \(attachment[.isValid]) = true AND \(attachment[.variant]) = \(Attachment.Variant.standard) AND + \(attachment[.sourceFilename]) <> 'session-audio-message' AND \(interaction[.threadId]) = \(threadId) """) } From 13370dbb9e1c7a97fb6098e87a6cb865cd0d3918 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Tue, 12 Jul 2022 15:31:54 +1000 Subject: [PATCH 10/19] add comments --- Session/Media Viewing & Editing/MediaGalleryViewModel.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift index 41d7130db..12417f8ec 100644 --- a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift +++ b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift @@ -264,6 +264,7 @@ public class MediaGalleryViewModel { \(interaction[.threadId]) = \(threadId) """) case .document: + // FIXME: Remove "\(attachment[.sourceFilename]) <> 'session-audio-message'" when all platforms send the voice message properly return SQL(""" \(attachment[.isVisualMedia]) = false AND \(attachment[.isValid]) = true AND From 13ded2d4b29600addb697db53195e16fcf06d24c Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 13 Jul 2022 13:23:17 +1000 Subject: [PATCH 11/19] minor fix --- Session/Meta/Translations/en.lproj/Localizable.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index c26840e8d..b8ffaf764 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -661,7 +661,7 @@ "MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages"; "MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app"; "MEDIA_TAB_TITLE" = "Media"; -"DOCUMENT_TAB_TITLE" = "Document"; +"DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; "DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; From bc16e3da4b495022e817481b8280687ed44416d0 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Mon, 8 Aug 2022 17:01:36 +1000 Subject: [PATCH 12/19] fix: shift the threadId filter line above the variant and sourceFilename lines --- Session/Media Viewing & Editing/MediaGalleryViewModel.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift index 12417f8ec..31e59614a 100644 --- a/Session/Media Viewing & Editing/MediaGalleryViewModel.swift +++ b/Session/Media Viewing & Editing/MediaGalleryViewModel.swift @@ -268,9 +268,9 @@ public class MediaGalleryViewModel { return SQL(""" \(attachment[.isVisualMedia]) = false AND \(attachment[.isValid]) = true AND + \(interaction[.threadId]) = \(threadId) AND \(attachment[.variant]) = \(Attachment.Variant.standard) AND - \(attachment[.sourceFilename]) <> 'session-audio-message' AND - \(interaction[.threadId]) = \(threadId) + \(attachment[.sourceFilename]) <> 'session-audio-message' """) } } From 8c74ecbe63247c9888e7c912906194c85d278521 Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Mon, 8 Aug 2022 17:07:58 +1000 Subject: [PATCH 13/19] add localised strings --- Session/Meta/Translations/de.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/es.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/fa.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/fi.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/fr.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/hi.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/hr.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/id-ID.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/it.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/ja.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/nl.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/pl.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/pt_BR.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/ru.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/si.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/sk.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/sv.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/th.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/vi-VN.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/zh-Hant.lproj/Localizable.strings | 4 ++++ Session/Meta/Translations/zh_CN.lproj/Localizable.strings | 4 ++++ 21 files changed, 84 insertions(+) diff --git a/Session/Meta/Translations/de.lproj/Localizable.strings b/Session/Meta/Translations/de.lproj/Localizable.strings index d12868e54..a6628324c 100644 --- a/Session/Meta/Translations/de.lproj/Localizable.strings +++ b/Session/Meta/Translations/de.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/es.lproj/Localizable.strings b/Session/Meta/Translations/es.lproj/Localizable.strings index e5d4f9d44..02392a086 100644 --- a/Session/Meta/Translations/es.lproj/Localizable.strings +++ b/Session/Meta/Translations/es.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/fa.lproj/Localizable.strings b/Session/Meta/Translations/fa.lproj/Localizable.strings index f333f1b70..f0e827093 100644 --- a/Session/Meta/Translations/fa.lproj/Localizable.strings +++ b/Session/Meta/Translations/fa.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/fi.lproj/Localizable.strings b/Session/Meta/Translations/fi.lproj/Localizable.strings index b9e9b8267..598f8ee8b 100644 --- a/Session/Meta/Translations/fi.lproj/Localizable.strings +++ b/Session/Meta/Translations/fi.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/fr.lproj/Localizable.strings b/Session/Meta/Translations/fr.lproj/Localizable.strings index f99011932..c2b65a5e1 100644 --- a/Session/Meta/Translations/fr.lproj/Localizable.strings +++ b/Session/Meta/Translations/fr.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/hi.lproj/Localizable.strings b/Session/Meta/Translations/hi.lproj/Localizable.strings index eca955776..f2352608d 100644 --- a/Session/Meta/Translations/hi.lproj/Localizable.strings +++ b/Session/Meta/Translations/hi.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/hr.lproj/Localizable.strings b/Session/Meta/Translations/hr.lproj/Localizable.strings index bb4556500..3e26705f5 100644 --- a/Session/Meta/Translations/hr.lproj/Localizable.strings +++ b/Session/Meta/Translations/hr.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/id-ID.lproj/Localizable.strings b/Session/Meta/Translations/id-ID.lproj/Localizable.strings index 6322bf9f2..991df78f3 100644 --- a/Session/Meta/Translations/id-ID.lproj/Localizable.strings +++ b/Session/Meta/Translations/id-ID.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/it.lproj/Localizable.strings b/Session/Meta/Translations/it.lproj/Localizable.strings index eedf2137b..deed6a138 100644 --- a/Session/Meta/Translations/it.lproj/Localizable.strings +++ b/Session/Meta/Translations/it.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/ja.lproj/Localizable.strings b/Session/Meta/Translations/ja.lproj/Localizable.strings index 253f72de8..d1825cc2f 100644 --- a/Session/Meta/Translations/ja.lproj/Localizable.strings +++ b/Session/Meta/Translations/ja.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/nl.lproj/Localizable.strings b/Session/Meta/Translations/nl.lproj/Localizable.strings index d58ebff00..b99947c74 100644 --- a/Session/Meta/Translations/nl.lproj/Localizable.strings +++ b/Session/Meta/Translations/nl.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/pl.lproj/Localizable.strings b/Session/Meta/Translations/pl.lproj/Localizable.strings index 5a08f7e35..4660f817d 100644 --- a/Session/Meta/Translations/pl.lproj/Localizable.strings +++ b/Session/Meta/Translations/pl.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings index a848b7587..309641933 100644 --- a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings +++ b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/ru.lproj/Localizable.strings b/Session/Meta/Translations/ru.lproj/Localizable.strings index a3984387e..d2927bd5d 100644 --- a/Session/Meta/Translations/ru.lproj/Localizable.strings +++ b/Session/Meta/Translations/ru.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/si.lproj/Localizable.strings b/Session/Meta/Translations/si.lproj/Localizable.strings index cacf3f5d1..28c928651 100644 --- a/Session/Meta/Translations/si.lproj/Localizable.strings +++ b/Session/Meta/Translations/si.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/sk.lproj/Localizable.strings b/Session/Meta/Translations/sk.lproj/Localizable.strings index 102514ede..43a94850c 100644 --- a/Session/Meta/Translations/sk.lproj/Localizable.strings +++ b/Session/Meta/Translations/sk.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/sv.lproj/Localizable.strings b/Session/Meta/Translations/sv.lproj/Localizable.strings index da8933ad7..e0c229efe 100644 --- a/Session/Meta/Translations/sv.lproj/Localizable.strings +++ b/Session/Meta/Translations/sv.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/th.lproj/Localizable.strings b/Session/Meta/Translations/th.lproj/Localizable.strings index b8f6e41ac..0a4512b39 100644 --- a/Session/Meta/Translations/th.lproj/Localizable.strings +++ b/Session/Meta/Translations/th.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings index fd93b1f63..19cc82d1a 100644 --- a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings +++ b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings index f936369d4..eb0d69ac7 100644 --- a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; diff --git a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings index 9145c3fe8..082e4ee09 100644 --- a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings @@ -684,3 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"DOCUMENT_TAB_TITLE" = "Documents"; +"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; +"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; +"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…"; From ab3bbab656d8c7cc76e55de82c18e47a4ead753f Mon Sep 17 00:00:00 2001 From: Ryan Zhao Date: Mon, 8 Aug 2022 17:11:56 +1000 Subject: [PATCH 14/19] add one more localised string --- Session/Meta/Translations/de.lproj/Localizable.strings | 1 + Session/Meta/Translations/es.lproj/Localizable.strings | 1 + Session/Meta/Translations/fa.lproj/Localizable.strings | 1 + Session/Meta/Translations/fi.lproj/Localizable.strings | 1 + Session/Meta/Translations/fr.lproj/Localizable.strings | 1 + Session/Meta/Translations/hi.lproj/Localizable.strings | 1 + Session/Meta/Translations/hr.lproj/Localizable.strings | 1 + Session/Meta/Translations/id-ID.lproj/Localizable.strings | 1 + Session/Meta/Translations/it.lproj/Localizable.strings | 1 + Session/Meta/Translations/ja.lproj/Localizable.strings | 1 + Session/Meta/Translations/nl.lproj/Localizable.strings | 1 + Session/Meta/Translations/pl.lproj/Localizable.strings | 1 + Session/Meta/Translations/pt_BR.lproj/Localizable.strings | 1 + Session/Meta/Translations/ru.lproj/Localizable.strings | 1 + Session/Meta/Translations/si.lproj/Localizable.strings | 1 + Session/Meta/Translations/sk.lproj/Localizable.strings | 1 + Session/Meta/Translations/sv.lproj/Localizable.strings | 1 + Session/Meta/Translations/th.lproj/Localizable.strings | 1 + Session/Meta/Translations/vi-VN.lproj/Localizable.strings | 1 + Session/Meta/Translations/zh-Hant.lproj/Localizable.strings | 1 + Session/Meta/Translations/zh_CN.lproj/Localizable.strings | 1 + 21 files changed, 21 insertions(+) diff --git a/Session/Meta/Translations/de.lproj/Localizable.strings b/Session/Meta/Translations/de.lproj/Localizable.strings index a6628324c..4f24b4be6 100644 --- a/Session/Meta/Translations/de.lproj/Localizable.strings +++ b/Session/Meta/Translations/de.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/es.lproj/Localizable.strings b/Session/Meta/Translations/es.lproj/Localizable.strings index 02392a086..a35952312 100644 --- a/Session/Meta/Translations/es.lproj/Localizable.strings +++ b/Session/Meta/Translations/es.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/fa.lproj/Localizable.strings b/Session/Meta/Translations/fa.lproj/Localizable.strings index f0e827093..9a95842fe 100644 --- a/Session/Meta/Translations/fa.lproj/Localizable.strings +++ b/Session/Meta/Translations/fa.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/fi.lproj/Localizable.strings b/Session/Meta/Translations/fi.lproj/Localizable.strings index 598f8ee8b..54461f20e 100644 --- a/Session/Meta/Translations/fi.lproj/Localizable.strings +++ b/Session/Meta/Translations/fi.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/fr.lproj/Localizable.strings b/Session/Meta/Translations/fr.lproj/Localizable.strings index c2b65a5e1..833967032 100644 --- a/Session/Meta/Translations/fr.lproj/Localizable.strings +++ b/Session/Meta/Translations/fr.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/hi.lproj/Localizable.strings b/Session/Meta/Translations/hi.lproj/Localizable.strings index f2352608d..2554f5d85 100644 --- a/Session/Meta/Translations/hi.lproj/Localizable.strings +++ b/Session/Meta/Translations/hi.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/hr.lproj/Localizable.strings b/Session/Meta/Translations/hr.lproj/Localizable.strings index 3e26705f5..fb1bbc470 100644 --- a/Session/Meta/Translations/hr.lproj/Localizable.strings +++ b/Session/Meta/Translations/hr.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/id-ID.lproj/Localizable.strings b/Session/Meta/Translations/id-ID.lproj/Localizable.strings index 991df78f3..7fe013f74 100644 --- a/Session/Meta/Translations/id-ID.lproj/Localizable.strings +++ b/Session/Meta/Translations/id-ID.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/it.lproj/Localizable.strings b/Session/Meta/Translations/it.lproj/Localizable.strings index deed6a138..e6f3f9284 100644 --- a/Session/Meta/Translations/it.lproj/Localizable.strings +++ b/Session/Meta/Translations/it.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/ja.lproj/Localizable.strings b/Session/Meta/Translations/ja.lproj/Localizable.strings index d1825cc2f..990ab7ced 100644 --- a/Session/Meta/Translations/ja.lproj/Localizable.strings +++ b/Session/Meta/Translations/ja.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/nl.lproj/Localizable.strings b/Session/Meta/Translations/nl.lproj/Localizable.strings index b99947c74..acbfcd4d0 100644 --- a/Session/Meta/Translations/nl.lproj/Localizable.strings +++ b/Session/Meta/Translations/nl.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/pl.lproj/Localizable.strings b/Session/Meta/Translations/pl.lproj/Localizable.strings index 4660f817d..7622a5018 100644 --- a/Session/Meta/Translations/pl.lproj/Localizable.strings +++ b/Session/Meta/Translations/pl.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings index 309641933..388381521 100644 --- a/Session/Meta/Translations/pt_BR.lproj/Localizable.strings +++ b/Session/Meta/Translations/pt_BR.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/ru.lproj/Localizable.strings b/Session/Meta/Translations/ru.lproj/Localizable.strings index d2927bd5d..53810f9ec 100644 --- a/Session/Meta/Translations/ru.lproj/Localizable.strings +++ b/Session/Meta/Translations/ru.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/si.lproj/Localizable.strings b/Session/Meta/Translations/si.lproj/Localizable.strings index 28c928651..570629c4b 100644 --- a/Session/Meta/Translations/si.lproj/Localizable.strings +++ b/Session/Meta/Translations/si.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/sk.lproj/Localizable.strings b/Session/Meta/Translations/sk.lproj/Localizable.strings index 43a94850c..cf6afe8c9 100644 --- a/Session/Meta/Translations/sk.lproj/Localizable.strings +++ b/Session/Meta/Translations/sk.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/sv.lproj/Localizable.strings b/Session/Meta/Translations/sv.lproj/Localizable.strings index e0c229efe..a20bc541a 100644 --- a/Session/Meta/Translations/sv.lproj/Localizable.strings +++ b/Session/Meta/Translations/sv.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/th.lproj/Localizable.strings b/Session/Meta/Translations/th.lproj/Localizable.strings index 0a4512b39..f4497af7f 100644 --- a/Session/Meta/Translations/th.lproj/Localizable.strings +++ b/Session/Meta/Translations/th.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings index 19cc82d1a..66e6011e5 100644 --- a/Session/Meta/Translations/vi-VN.lproj/Localizable.strings +++ b/Session/Meta/Translations/vi-VN.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings index eb0d69ac7..42ecd2c27 100644 --- a/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh-Hant.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; diff --git a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings index 082e4ee09..b18755fe5 100644 --- a/Session/Meta/Translations/zh_CN.lproj/Localizable.strings +++ b/Session/Meta/Translations/zh_CN.lproj/Localizable.strings @@ -684,6 +684,7 @@ "SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send."; "INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again."; "INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again."; +"MEDIA_TAB_TITLE" = "Media"; "DOCUMENT_TAB_TITLE" = "Documents"; "DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation."; "DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…"; From 402bee2d3816e8bab31dbfdea866bfa8e99623f3 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Tue, 9 Aug 2022 12:39:26 +1000 Subject: [PATCH 15/19] feat: add action for document section --- Podfile.lock | 2 +- .../AllMediaViewController.swift | 34 +++++++++++++++++++ .../DocumentTitleViewController.swift | 29 ++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index 5e750d933..37f4ac9c5 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -244,4 +244,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: f0857369c4831b2e5c1946345e76e493f3286805 -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/Session/Media Viewing & Editing/AllMediaViewController.swift b/Session/Media Viewing & Editing/AllMediaViewController.swift index 121db8b43..aa7b7a072 100644 --- a/Session/Media Viewing & Editing/AllMediaViewController.swift +++ b/Session/Media Viewing & Editing/AllMediaViewController.swift @@ -34,6 +34,7 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS self.mediaTitleViewController = mediaTitleViewController self.documentTitleViewController = documentTitleViewController super.init(nibName: nil, bundle: nil) + self.documentTitleViewController.delegate = self } required init?(coder: NSCoder) { @@ -98,3 +99,36 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS dismiss(animated: true, completion: nil) } } + +// 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) + } +} + + diff --git a/Session/Media Viewing & Editing/DocumentTitleViewController.swift b/Session/Media Viewing & Editing/DocumentTitleViewController.swift index 9e937ab4b..af790fb89 100644 --- a/Session/Media Viewing & Editing/DocumentTitleViewController.swift +++ b/Session/Media Viewing & Editing/DocumentTitleViewController.swift @@ -23,6 +23,8 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, private var isAutoLoadingNextPage: Bool = false private var currentTargetOffset: CGPoint? + public var delegate: DocumentTileViewControllerDelegate? + // MARK: - Initialization init(viewModel: MediaGalleryViewModel) { @@ -313,9 +315,29 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate, public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: false) + let attachment: Attachment = self.viewModel.galleryData[indexPath.section].elements[indexPath.row].attachment + guard let originalFilePath: String = attachment.originalFilePath else { return } + + let fileUrl: URL = URL(fileURLWithPath: originalFilePath) + + // Open a preview of the document for text, pdf or microsoft files + if + attachment.isText || + attachment.isMicrosoftDoc || + attachment.contentType == OWSMimeTypeApplicationPdf + { + + delegate?.preview(fileUrl: fileUrl) + return + } + + // Otherwise share the file + delegate?.share(fileUrl: fileUrl) } } +// MARK: - View + class DocumentCell: UITableViewCell { // MARK: - Initialization @@ -472,3 +494,10 @@ class DocumentStaticHeaderView: UIView { self.label.text = title } } + +// MARK: - DocumentTitleViewControllerDelegate + +public protocol DocumentTileViewControllerDelegate: AnyObject { + func share(fileUrl: URL) + func preview(fileUrl: URL) +} From 1fa505d1a24d75c25dbddf995db4b86264739d76 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Tue, 9 Aug 2022 16:00:05 +1000 Subject: [PATCH 16/19] feat: show media detail --- .../AllMediaViewController.swift | 20 +++++++++++++++++++ .../MediaTileViewController.swift | 18 +++++++++-------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/Session/Media Viewing & Editing/AllMediaViewController.swift b/Session/Media Viewing & Editing/AllMediaViewController.swift index aa7b7a072..e91e51af8 100644 --- a/Session/Media Viewing & Editing/AllMediaViewController.swift +++ b/Session/Media Viewing & Editing/AllMediaViewController.swift @@ -34,6 +34,7 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS self.mediaTitleViewController = mediaTitleViewController self.documentTitleViewController = documentTitleViewController super.init(nibName: nil, bundle: nil) + self.mediaTitleViewController.delegate = self self.documentTitleViewController.delegate = self } @@ -131,4 +132,23 @@ extension AllMediaViewController: DocumentTileViewControllerDelegate { } } +// MARK: - DocumentTitleViewControllerDelegate + +extension AllMediaViewController: MediaTileViewControllerDelegate { + public func presentdetailViewController(_ detailViewController: UIViewController, animated: Bool) { + self.present(detailViewController, animated: animated) + } +} + +// 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) + } +} diff --git a/Session/Media Viewing & Editing/MediaTileViewController.swift b/Session/Media Viewing & Editing/MediaTileViewController.swift index 1085b574a..aa1770062 100644 --- a/Session/Media Viewing & Editing/MediaTileViewController.swift +++ b/Session/Media Viewing & Editing/MediaTileViewController.swift @@ -17,12 +17,14 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour static let footerBarHeight: CGFloat = 40 static let loadMoreHeaderHeight: CGFloat = 100 - private let viewModel: MediaGalleryViewModel + public let viewModel: MediaGalleryViewModel private var hasLoadedInitialData: Bool = false private var didFinishInitialLayout: Bool = false private var isAutoLoadingNextPage: Bool = false private var currentTargetOffset: CGPoint? + public var delegate: MediaTileViewControllerDelegate? + var isInBatchSelectMode = false { didSet { collectionView.allowsMultipleSelection = isInBatchSelectMode @@ -492,12 +494,6 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour // [ConversationSettingsView] // [ConversationView] // - guard - let viewControllers: [UIViewController] = self.navigationController?.viewControllers, - viewControllers.count > 1, - viewControllers[viewControllers.count - 2] is OWSConversationSettingsViewController - else { return } - let detailViewController: UIViewController? = MediaGalleryViewModel.createDetailViewController( for: self.viewModel.threadId, threadVariant: self.viewModel.threadVariant, @@ -508,7 +504,7 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour guard let detailViewController: UIViewController = detailViewController else { return } - self.present(detailViewController, animated: true) + delegate?.presentdetailViewController(detailViewController, animated: true) return } @@ -923,3 +919,9 @@ extension MediaTileViewController: MediaPresentationContextProvider { return self.navigationController?.navigationBar.generateSnapshot(in: coordinateSpace) } } + +// MARK: - MediaTileViewControllerDelegate + +public protocol MediaTileViewControllerDelegate: AnyObject { + func presentdetailViewController(_ detailViewController: UIViewController, animated: Bool) +} From a162f324b1507e6f6bec80071304a5defca78705 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 10 Aug 2022 13:32:53 +1000 Subject: [PATCH 17/19] feat: add select button and delete function in media title screen --- .../AllMediaViewController.swift | 44 +++++++++++++++++++ .../MediaTileViewController.swift | 32 ++------------ 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/Session/Media Viewing & Editing/AllMediaViewController.swift b/Session/Media Viewing & Editing/AllMediaViewController.swift index e91e51af8..77d61f636 100644 --- a/Session/Media Viewing & Editing/AllMediaViewController.swift +++ b/Session/Media Viewing & Editing/AllMediaViewController.swift @@ -18,10 +18,13 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS 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 } ] return TabBar(tabs: tabs) @@ -99,6 +102,24 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS @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 @@ -138,6 +159,29 @@ 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: - MediaPresentationContextProvider diff --git a/Session/Media Viewing & Editing/MediaTileViewController.swift b/Session/Media Viewing & Editing/MediaTileViewController.swift index aa1770062..13dcb94e0 100644 --- a/Session/Media Viewing & Editing/MediaTileViewController.swift +++ b/Session/Media Viewing & Editing/MediaTileViewController.swift @@ -271,6 +271,7 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour guard hasLoadedInitialData else { self.hasLoadedInitialData = true self.viewModel.updateGalleryData(updatedGalleryData) + self.updateSelectButton(updatedData: updatedGalleryData, inBatchSelectMode: isInBatchSelectMode) UIView.performWithoutAnimation { self.collectionView.reloadData() @@ -586,26 +587,7 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour } 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) - ) - } + delegate?.updateSelectButton(updatedData: updatedData, inBatchSelectMode: inBatchSelectMode) } @objc func didTapSelect(_ sender: Any) { @@ -620,13 +602,6 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour // Ensure toolbar doesn't cover bottom row. self?.collectionView.contentInset.bottom += MediaTileViewController.footerBarHeight }, completion: nil) - - // disabled until at least one item is selected - self.deleteButton.isEnabled = false - - // 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) { @@ -646,8 +621,6 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour self?.collectionView.contentInset.bottom -= MediaTileViewController.footerBarHeight }, completion: nil) - self.navigationItem.hidesBackButton = false - // Deselect any selected collectionView.indexPathsForSelectedItems?.forEach { collectionView.deselectItem(at: $0, animated: false)} } @@ -924,4 +897,5 @@ extension MediaTileViewController: MediaPresentationContextProvider { public protocol MediaTileViewControllerDelegate: AnyObject { func presentdetailViewController(_ detailViewController: UIViewController, animated: Bool) + func updateSelectButton(updatedData: [MediaGalleryViewModel.SectionModel], inBatchSelectMode: Bool) } From fa1a67d9572966bfac20e08f8804f8ff248258cf Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Wed, 10 Aug 2022 13:41:24 +1000 Subject: [PATCH 18/19] feat: show all media view with media & document sections from tapping All Media nav button --- .../AllMediaViewController.swift | 13 +++++++++++++ .../MediaPageViewController.swift | 8 ++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Session/Media Viewing & Editing/AllMediaViewController.swift b/Session/Media Viewing & Editing/AllMediaViewController.swift index 77d61f636..ecf9bef7a 100644 --- a/Session/Media Viewing & Editing/AllMediaViewController.swift +++ b/Session/Media Viewing & Editing/AllMediaViewController.swift @@ -184,6 +184,19 @@ extension AllMediaViewController: MediaTileViewControllerDelegate { } } +// 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 { diff --git a/Session/Media Viewing & Editing/MediaPageViewController.swift b/Session/Media Viewing & Editing/MediaPageViewController.swift index 53ecb371b..42639b2a9 100644 --- a/Session/Media Viewing & Editing/MediaPageViewController.swift +++ b/Session/Media Viewing & Editing/MediaPageViewController.swift @@ -458,7 +458,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou // MediaTileViewController then just pop/dismiss the screen guard let presentingNavController: UINavigationController = (self.presentingViewController as? UINavigationController), - !(presentingNavController.viewControllers.last is MediaTileViewController) + !(presentingNavController.viewControllers.last is AllMediaViewController) else { guard self.navigationController?.viewControllers.count == 1 else { self.navigationController?.popViewController(animated: true) @@ -471,7 +471,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou // Otherwise if we came via the conversation screen we need to push a new // instance of MediaTileViewController - let tileViewController: MediaTileViewController = MediaGalleryViewModel.createMediaTileViewController( + let allMediaViewController: AllMediaViewController = MediaGalleryViewModel.createAllMediaViewController( threadId: self.viewModel.threadId, threadVariant: self.viewModel.threadVariant, focusedAttachmentId: currentItem.attachment.id, @@ -479,9 +479,9 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou ) let navController: MediaGalleryNavigationController = MediaGalleryNavigationController() - navController.viewControllers = [tileViewController] + navController.viewControllers = [allMediaViewController] navController.modalPresentationStyle = .overFullScreen - navController.transitioningDelegate = tileViewController + navController.transitioningDelegate = allMediaViewController self.navigationController?.present(navController, animated: true) } From 2acc32a44b26edc7a324d563c1af96a80fd9ba18 Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Thu, 11 Aug 2022 15:01:23 +1000 Subject: [PATCH 19/19] Fixed the broken MediaDetail <-> MediaTile custom transition --- .../AllMediaViewController.swift | 6 +++ .../MediaTileViewController.swift | 43 +++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Session/Media Viewing & Editing/AllMediaViewController.swift b/Session/Media Viewing & Editing/AllMediaViewController.swift index ecf9bef7a..6090b7404 100644 --- a/Session/Media Viewing & Editing/AllMediaViewController.swift +++ b/Session/Media Viewing & Editing/AllMediaViewController.swift @@ -36,9 +36,13 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS 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) { @@ -66,6 +70,8 @@ public class AllMediaViewController: UIViewController, UIPageViewControllerDataS 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) diff --git a/Session/Media Viewing & Editing/MediaTileViewController.swift b/Session/Media Viewing & Editing/MediaTileViewController.swift index 13dcb94e0..44d1bb4e3 100644 --- a/Session/Media Viewing & Editing/MediaTileViewController.swift +++ b/Session/Media Viewing & Editing/MediaTileViewController.swift @@ -201,8 +201,35 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour guard let focusedIndexPath: IndexPath = self.viewModel.focusedIndexPath else { return } Logger.debug("scrolling to focused item at indexPath: \(focusedIndexPath)") + + // Note: For some reason 'scrollToItem' doesn't always work properly so we need to manually + // calculate what the offset should be to do the initial scroll self.view.layoutIfNeeded() - self.collectionView.scrollToItem(at: focusedIndexPath, at: .centeredVertically, animated: false) + + let availableHeight: CGFloat = { + // Note: This height will be set before we have properly performed a layout and fitted + // this screen within it's parent UIPagedViewController so we need to try to calculate + // the "actual" height of the collection view + var finalHeight: CGFloat = self.collectionView.frame.height + + if let navController: UINavigationController = self.parent?.navigationController { + finalHeight -= navController.navigationBar.frame.height + finalHeight -= (UIApplication.shared.keyWindow?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0) + } + + if let tabBar: TabBar = self.parent?.parent?.view.subviews.first as? TabBar { + finalHeight -= tabBar.frame.height + } + + return finalHeight + }() + let focusedRect: CGRect = (self.collectionView.layoutAttributesForItem(at: focusedIndexPath)?.frame) + .defaulting(to: .zero) + self.collectionView.contentOffset = CGPoint( + x: 0, + y: (focusedRect.origin.y - (availableHeight / 2) + (focusedRect.height / 2)) + ) + self.collectionView.collectionViewLayout.invalidateLayout() // Now that the data has loaded we need to check if either of the "load more" sections are // visible and trigger them if so @@ -832,7 +859,12 @@ class GalleryGridCellItem: PhotoGridItem { extension MediaTileViewController: UIViewControllerTransitioningDelegate { public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { - guard self == presented || self.navigationController == presented else { return nil } + guard + self == presented || + self.navigationController == presented || + self.parent == presented || + self.parent?.navigationController == presented + else { return nil } guard let focusedIndexPath: IndexPath = self.viewModel.focusedIndexPath else { return nil } return MediaDismissAnimationController( @@ -841,7 +873,12 @@ extension MediaTileViewController: UIViewControllerTransitioningDelegate { } public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { - guard self == dismissed || self.navigationController == dismissed else { return nil } + guard + self == dismissed || + self.navigationController == dismissed || + self.parent == dismissed || + self.parent?.navigationController == dismissed + else { return nil } guard let focusedIndexPath: IndexPath = self.viewModel.focusedIndexPath else { return nil } return MediaZoomAnimationController(