diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 5c628fd87..68bcc733c 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -1485,7 +1485,7 @@ typedef enum : NSUInteger { OWSConversationSettingsViewController *settingsVC = [OWSConversationSettingsViewController new]; settingsVC.conversationSettingsViewDelegate = self; - [settingsVC configureWithThread:self.thread]; + [settingsVC configureWithThread:self.thread uiDatabaseConnection:self.uiDatabaseConnection]; settingsVC.showVerificationOnAppear = showVerification; [self.navigationController pushViewController:settingsVC animated:YES]; } @@ -2034,10 +2034,9 @@ typedef enum : NSUInteger { TSMessage *mediaMessage = (TSMessage *)viewItem.interaction; MediaGalleryViewController *vc = [[MediaGalleryViewController alloc] initWithThread:self.thread - mediaMessage:mediaMessage uiDatabaseConnection:self.uiDatabaseConnection]; - [vc presentDetailViewFromViewController:self replacingView:imageView]; + [vc presentDetailViewFromViewController:self mediaMessage:mediaMessage replacingView:imageView]; } - (void)didTapVideoViewItem:(ConversationViewItem *)viewItem @@ -2057,10 +2056,9 @@ typedef enum : NSUInteger { TSMessage *mediaMessage = (TSMessage *)viewItem.interaction; MediaGalleryViewController *vc = [[MediaGalleryViewController alloc] initWithThread:self.thread - mediaMessage:mediaMessage uiDatabaseConnection:self.uiDatabaseConnection]; - [vc presentDetailViewFromViewController:self replacingView:imageView]; + [vc presentDetailViewFromViewController:self mediaMessage:mediaMessage replacingView:imageView]; } - (void)didTapAudioViewItem:(ConversationViewItem *)viewItem attachmentStream:(TSAttachmentStream *)attachmentStream diff --git a/Signal/src/ViewControllers/MediaGalleryViewController.swift b/Signal/src/ViewControllers/MediaGalleryViewController.swift index b8480f598..6f331c84a 100644 --- a/Signal/src/ViewControllers/MediaGalleryViewController.swift +++ b/Signal/src/ViewControllers/MediaGalleryViewController.swift @@ -141,40 +141,6 @@ public struct GalleryDate: Hashable, Comparable, Equatable { public static func == (lhs: GalleryDate, rhs: GalleryDate) -> Bool { return lhs.month == rhs.month && lhs.year == rhs.year } - -// // MARK: Sequence / IteratorProtocol -// public func until(_ toDate: GalleryDate) -> GalleryDateSequence { -// return GalleryDateSequence(from: self, to: toDate) -// } -// -// public class GalleryDateSequence: Sequence, IteratorProtocol { -// public typealias Element = GalleryDate -// -// var currentDate: GalleryDate -// let toDate: GalleryDate -// -// init(from: GalleryDate, to: GalleryDate) { -// self.currentDate = from -// self.toDate = to -// } -// -// public func next() -> GalleryDate? { -// guard currentDate < toDate else { -// return nil -// } -// -// let nextDate: GalleryDate = { -// if currentDate.month == 12 { -// return GalleryDate(year: currentDate.year + 1, month: 1) -// } else { -// return GalleryDate(year: currentDate.year, month: currentDate.month + 1) -// } -// }() -// currentDate = nextDate -// return nextDate -// } -// } - } protocol MediaGalleryDataSource: class { @@ -192,33 +158,30 @@ protocol MediaGalleryDataSource: class { func galleryItem(before currentItem: MediaGalleryItem) -> MediaGalleryItem? func galleryItem(after currentItem: MediaGalleryItem) -> MediaGalleryItem? - func showAllMedia() - // TODO this doesn't seem very "data-source" + func showAllMedia(focusedItem: MediaGalleryItem) func dismissSelf(animated isAnimated: Bool, completion: (() -> Void)?) } class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource, MediaTileViewControllerDelegate { private var pageViewController: MediaPageViewController? - // private let tileViewController: MediaTileViewController - // + private let uiDatabaseConnection: YapDatabaseConnection private let mediaGalleryFinder: OWSMediaGalleryFinder - // FIXME get rid of `!` - private var initialGalleryItem: MediaGalleryItem! + private var initialDetailItem: MediaGalleryItem? private let thread: TSThread private let includeGallery: Bool // we start with a small range size for quick loading. private let fetchRangeSize: UInt = 10 - convenience init(thread: TSThread, mediaMessage: TSMessage, uiDatabaseConnection: YapDatabaseConnection) { - self.init(thread: thread, mediaMessage: mediaMessage, uiDatabaseConnection: uiDatabaseConnection, includeGallery: true) + convenience init(thread: TSThread, uiDatabaseConnection: YapDatabaseConnection) { + self.init(thread: thread, uiDatabaseConnection: uiDatabaseConnection, includeGallery: true) } - init(thread: TSThread, mediaMessage: TSMessage, uiDatabaseConnection: YapDatabaseConnection, includeGallery: Bool) { + init(thread: TSThread, uiDatabaseConnection: YapDatabaseConnection, includeGallery: Bool) { self.thread = thread assert(uiDatabaseConnection.isInLongLivedReadTransaction()) self.uiDatabaseConnection = uiDatabaseConnection @@ -226,14 +189,6 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource self.mediaGalleryFinder = OWSMediaGalleryFinder(thread: thread) super.init(nibName: nil, bundle: nil) - - uiDatabaseConnection.read { transaction in - self.initialGalleryItem = self.buildGalleryItem(message: mediaMessage, transaction: transaction)! - } - - // For a speedy load, we only fetch a few items on either side of - // the initial message - ensureGalleryItemsLoaded(.around, item: self.initialGalleryItem, amount: 10) } required init?(coder aDecoder: NSCoder) { @@ -268,6 +223,10 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource // MARK: Present/Dismiss + private var currentPage: MediaGalleryPage? { + return self.pageViewController!.currentPage + } + private var replacingView: UIView? private var presentationView: UIImageView! private var presentationViewConstraints: [NSLayoutConstraint] = [] @@ -275,9 +234,27 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource // TODO rename to replacingOriginRect private var originRect: CGRect? - public func presentDetailView(fromViewController: UIViewController, replacingView: UIView) { + public func presentDetailView(fromViewController: UIViewController, mediaMessage: TSMessage, replacingView: UIView) { + var galleryItem: MediaGalleryItem? + uiDatabaseConnection.read { transaction in + galleryItem = self.buildGalleryItem(message: mediaMessage, transaction: transaction)! + } - let pageViewController = MediaPageViewController(initialItem: self.initialGalleryItem, mediaGalleryDataSource: self, uiDatabaseConnection: self.uiDatabaseConnection, includeGallery: self.includeGallery) + guard let initialDetailItem = galleryItem else { + owsFail("\(logTag) in \(#function) unexpectedly failed to build initialDetailItem.") + return + } + + presentDetailView(fromViewController: fromViewController, initialDetailItem: initialDetailItem, replacingView: replacingView) + } + + public func presentDetailView(fromViewController: UIViewController, initialDetailItem: MediaGalleryItem, replacingView: UIView) { + // For a speedy load, we only fetch a few items on either side of + // the initial message + ensureGalleryItemsLoaded(.around, item: initialDetailItem, amount: 10) + self.initialDetailItem = initialDetailItem + + let pageViewController = MediaPageViewController(initialItem: initialDetailItem, mediaGalleryDataSource: self, uiDatabaseConnection: self.uiDatabaseConnection, includeGallery: self.includeGallery) self.pageViewController = pageViewController self.setViewControllers([pageViewController], animated: false) @@ -289,7 +266,7 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource // loadView hasn't necessarily been called yet. self.loadViewIfNeeded() - self.presentationView.image = self.initialGalleryItem.fullSizedImage + self.presentationView.image = initialDetailItem.fullSizedImage self.applyInitialMediaViewConstraints() // We want to animate the tapped media from it's position in the previous VC @@ -360,8 +337,51 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource } } - private var currentPage: MediaGalleryPage? { - return self.pageViewController!.currentPage + // If we're using a navigationController other than self to present the views + // e.g. the conversation settings view controller + var fromNavController: UINavigationController? + + func pushTileView(fromNavController: UINavigationController) { + var mostRecentItem: MediaGalleryItem? + self.uiDatabaseConnection.read { transaction in + if let message = self.mediaGalleryFinder.mostRecentMediaMessage(transaction: transaction) { + mostRecentItem = self.buildGalleryItem(message: message, transaction: transaction) + } + } + + if let mostRecentItem = mostRecentItem { + mediaTileViewController.focusedItem = mostRecentItem + ensureGalleryItemsLoaded(.around, item: mostRecentItem, amount: 100) + } + self.fromNavController = fromNavController + fromNavController.pushViewController(mediaTileViewController, animated: true) + } + + func showAllMedia(focusedItem: MediaGalleryItem) { + // TODO fancy animation - zoom media item into it's tile in the all media grid + ensureGalleryItemsLoaded(.around, item: focusedItem, amount: 100) + + if let fromNavController = self.fromNavController { + // If from conversation settings view, we've already pushed + fromNavController.popViewController(animated: true) + } else { + // If from conversation view + mediaTileViewController.focusedItem = focusedItem + self.pushViewController(mediaTileViewController, animated: true) + } + } + + // MARK: MediaTileViewControllerDelegate + + func mediaTileViewController(_ viewController: MediaTileViewController, didTapView tappedView: UIView, mediaGalleryItem: MediaGalleryItem) { + if self.fromNavController != nil { + // If from conversation settings view, we've already pushed + self.presentDetailView(fromViewController: mediaTileViewController, initialDetailItem: mediaGalleryItem, replacingView: tappedView) + } else { + // If from conversation view + self.pageViewController!.currentItem = mediaGalleryItem + self.popViewController(animated: true) + } } public func dismissSelf(animated isAnimated: Bool, completion: (() -> Void)? = nil) { @@ -384,7 +404,7 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource // Move the presentationView back to it's initial position, i.e. where // it sits on the screen in the conversation view. - let changedItems = currentPage.galleryItem != initialGalleryItem + let changedItems = currentPage.galleryItem != self.initialDetailItem if changedItems { self.presentationView.image = currentPage.image self.applyOffscreenMediaViewConstraints() @@ -493,16 +513,11 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource // MARK: MediaGalleryDataSource - func showAllMedia() { - - ensureGalleryItemsLoaded(.around, item: self.initialGalleryItem, amount: 100) - - // TODO fancy animation - zoom media item into it's tile in the all media grid - let allMediaController = MediaTileViewController(mediaGalleryDataSource: self, uiDatabaseConnection: self.uiDatabaseConnection) - allMediaController.delegate = self - - self.pushViewController(allMediaController, animated: true) - } + lazy var mediaTileViewController: MediaTileViewController = { + let vc = MediaTileViewController(mediaGalleryDataSource: self, uiDatabaseConnection: self.uiDatabaseConnection) + vc.delegate = self + return vc + }() var galleryItems: [MediaGalleryItem] = [] var sections: [GalleryDate: [MediaGalleryItem]] = [:] @@ -689,12 +704,4 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource } return Int(count) } - - // MARK: MediaTileViewControllerDelegate - - func mediaTileViewController(_ viewController: MediaTileViewController, didTapMediaGalleryItem mediaGalleryItem: MediaGalleryItem) { - self.pageViewController!.currentItem = mediaGalleryItem - self.popViewController(animated: true) - } - } diff --git a/Signal/src/ViewControllers/MediaPageViewController.swift b/Signal/src/ViewControllers/MediaPageViewController.swift index 70ac39b3a..2ba54941b 100644 --- a/Signal/src/ViewControllers/MediaPageViewController.swift +++ b/Signal/src/ViewControllers/MediaPageViewController.swift @@ -181,7 +181,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou public func didPressAllMediaButton(sender: Any) { Logger.debug("\(logTag) in \(#function)") - self.mediaGalleryDataSource.showAllMedia() + self.mediaGalleryDataSource.showAllMedia(focusedItem: currentItem) } @objc diff --git a/Signal/src/ViewControllers/MediaTileViewController.swift b/Signal/src/ViewControllers/MediaTileViewController.swift index 254316d01..0769f7029 100644 --- a/Signal/src/ViewControllers/MediaTileViewController.swift +++ b/Signal/src/ViewControllers/MediaTileViewController.swift @@ -5,7 +5,7 @@ import Foundation public protocol MediaTileViewControllerDelegate: class { - func mediaTileViewController(_ viewController: MediaTileViewController, didTapMediaGalleryItem mediaGalleryItem: MediaGalleryItem) + func mediaTileViewController(_ viewController: MediaTileViewController, didTapView tappedView: UIView, mediaGalleryItem: MediaGalleryItem) } public class MediaTileViewController: UICollectionViewController, MediaGalleryCellDelegate { @@ -19,6 +19,7 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe private var galleryDates: [GalleryDate] { return mediaGalleryDataSource.sectionDates } + public var focusedItem: MediaGalleryItem? private let uiDatabaseConnection: YapDatabaseConnection @@ -81,6 +82,33 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe scrollToBottom(animated: false) } + private func indexPath(galleryItem: MediaGalleryItem) -> IndexPath? { + guard let sectionIdx = galleryDates.index(of: galleryItem.galleryDate) else { + return nil + } + guard let rowIdx = galleryItems[galleryItem.galleryDate]!.index(of: galleryItem) else { + return nil + } + + return IndexPath(row: rowIdx, section: sectionIdx + 1) + } + + override public func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + guard let focusedItem = self.focusedItem else { + return + } + + guard let indexPath = self.indexPath(galleryItem: focusedItem) else { + owsFail("\(logTag) unexpectedly unable to find indexPath for focusedItem: \(focusedItem)") + return + } + + Logger.debug("\(logTag) scrolling to focused item at indexPath: \(indexPath)") + self.collectionView?.scrollToItem(at: indexPath, at: .centeredVertically, animated: false) + } + // MARK: UIColletionViewDelegate override public func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -234,7 +262,7 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryCe public func didTapCell(_ cell: MediaGalleryCell, item: MediaGalleryItem) { Logger.debug("\(logTag) in \(#function)") - self.delegate?.mediaTileViewController(self, didTapMediaGalleryItem: item) + self.delegate?.mediaTileViewController(self, didTapView: cell.imageView, mediaGalleryItem: item) } // MARK: Lazy Loading @@ -461,7 +489,7 @@ public class MediaGalleryCell: UICollectionViewCell { static let reuseIdentifier = "MediaGalleryCell" - private let imageView: UIImageView + public let imageView: UIImageView private var tapGesture: UITapGestureRecognizer! private var item: MediaGalleryItem? diff --git a/Signal/src/ViewControllers/MessageDetailViewController.swift b/Signal/src/ViewControllers/MessageDetailViewController.swift index 889c6255d..43ed95227 100644 --- a/Signal/src/ViewControllers/MessageDetailViewController.swift +++ b/Signal/src/ViewControllers/MessageDetailViewController.swift @@ -762,7 +762,7 @@ class MessageDetailViewController: OWSViewController, UIScrollViewDelegate, Medi return } - let mediaGalleryViewController = MediaGalleryViewController(thread: self.thread, mediaMessage: self.message, uiDatabaseConnection: self.uiDatabaseConnection, includeGallery: false) - mediaGalleryViewController.presentDetailView(fromViewController: self, replacingView: fromView) + let mediaGalleryViewController = MediaGalleryViewController(thread: self.thread, uiDatabaseConnection: self.uiDatabaseConnection, includeGallery: false) + mediaGalleryViewController.presentDetailView(fromViewController: self, mediaMessage: self.message, replacingView: fromView) } } diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.h b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.h index 85f0e507f..7d7542008 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.h +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.h @@ -9,6 +9,7 @@ NS_ASSUME_NONNULL_BEGIN @class TSThread; +@class YapDatabaseConnection; @interface OWSConversationSettingsViewController : OWSTableViewController @@ -16,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) BOOL showVerificationOnAppear; -- (void)configureWithThread:(TSThread *)thread; +- (void)configureWithThread:(TSThread *)thread uiDatabaseConnection:(YapDatabaseConnection *)uiDatabaseConnection; @end diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m index 6107ebe71..db5e3e5c3 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m @@ -40,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSConversationSettingsViewController () @property (nonatomic) TSThread *thread; +@property (nonatomic) YapDatabaseConnection *uiDatabaseConnection; @property (nonatomic) NSArray *disappearingMessagesDurations; @property (nonatomic) OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration; @@ -139,10 +140,11 @@ NS_ASSUME_NONNULL_BEGIN return [self.thread isKindOfClass:[TSGroupThread class]]; } -- (void)configureWithThread:(TSThread *)thread +- (void)configureWithThread:(TSThread *)thread uiDatabaseConnection:(YapDatabaseConnection *)uiDatabaseConnection { OWSAssert(thread); self.thread = thread; + self.uiDatabaseConnection = uiDatabaseConnection; if ([self.thread isKindOfClass:[TSContactThread class]]) { self.title = NSLocalizedString( @@ -262,6 +264,13 @@ NS_ASSUME_NONNULL_BEGIN mainSection.customHeaderView = [self mainSectionHeader]; mainSection.customHeaderHeight = @(100.f); + [mainSection addItem:[OWSTableItem itemWithCustomCellBlock:^{ + return [weakSelf disclosureCellWithName:MediaStrings.allMedia iconName:@"actionsheet_camera_roll_black"]; + } + actionBlock:^{ + [weakSelf showMediaGallery]; + }]]; + if ([self.thread isKindOfClass:[TSContactThread class]] && self.contactsManager.supportsContactEditing && !self.hasExistingContact) { [mainSection addItem:[OWSTableItem itemWithCustomCellBlock:^{ @@ -330,13 +339,6 @@ NS_ASSUME_NONNULL_BEGIN }]]; } - [mainSection addItem:[OWSTableItem itemWithCustomCellBlock:^{ - return [weakSelf disclosureCellWithName:MediaStrings.allMedia iconName:@"actionsheet_camera_roll_black"]; - } - actionBlock:^{ - [weakSelf showMediaGallery]; - }]]; - [mainSection addItem:[OWSTableItem itemWithCustomCellBlock:^{ UITableViewCell *cell = [UITableViewCell new]; @@ -1163,7 +1165,10 @@ NS_ASSUME_NONNULL_BEGIN { DDLogDebug(@"%@ in showMediaGallery", self.logTag); - // [[AllMediaViewController alloc] initWithThread:self.thread]; + MediaGalleryViewController *vc = + [[MediaGalleryViewController alloc] initWithThread:self.thread uiDatabaseConnection:self.uiDatabaseConnection]; + + [vc pushTileViewFromNavController:self.navigationController]; } #pragma mark - Notifications