Access Media Gallery from conversation settings

// FREEBIE
This commit is contained in:
Michael Kirk 2018-03-19 12:22:39 -04:00
parent f733c07d07
commit ae8dbeb8dd
7 changed files with 135 additions and 96 deletions

View File

@ -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

View File

@ -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)
}
}

View File

@ -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

View File

@ -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?

View File

@ -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)
}
}

View File

@ -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

View File

@ -40,6 +40,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface OWSConversationSettingsViewController () <ContactEditingDelegate, ContactsViewHelperDelegate>
@property (nonatomic) TSThread *thread;
@property (nonatomic) YapDatabaseConnection *uiDatabaseConnection;
@property (nonatomic) NSArray<NSNumber *> *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