From fabd3996c2ebf7e3616e73dd04f4d6b2185fd4fe Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 20 Feb 2019 12:44:30 -0700 Subject: [PATCH] pop view if message is deleted - use global ui database connection --- .../ConversationViewController.m | 16 +- .../ConversationSearchViewController.swift | 6 +- .../MediaGalleryViewController.swift | 239 ++++++++++++------ .../MediaPageViewController.swift | 4 +- .../MediaTileViewController.swift | 4 +- .../MessageDetailViewController.swift | 64 +++-- .../OWSConversationSettingsViewController.m | 1 - SignalMessaging/utils/Bench.swift | 20 +- .../src/Storage/OWSMediaGalleryFinder.h | 15 +- .../src/Storage/OWSMediaGalleryFinder.m | 21 +- .../src/Util/YapDatabase+Promise.swift | 32 +++ 11 files changed, 308 insertions(+), 114 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index a40183cd2..ce8f314aa 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -123,6 +123,7 @@ typedef enum : NSUInteger { ConversationViewCellDelegate, ConversationInputTextViewDelegate, MessageActionsDelegate, + MessageDetailViewDelegate, MenuActionsViewControllerDelegate, OWSMessageBubbleViewDelegate, UICollectionViewDelegate, @@ -1924,6 +1925,14 @@ typedef enum : NSUInteger { [self populateReplyForViewItem:conversationViewItem]; } +#pragma mark - MessageDetailViewDelegate + +- (void)detailViewMessageWasDeleted:(MessageDetailViewController *)messageDetailViewController +{ + OWSLogInfo(@""); + [self.navigationController popToViewController:self animated:YES]; +} + #pragma mark - MenuActionsViewControllerDelegate - (void)menuActionsDidHide:(MenuActionsViewController *)menuActionsViewController @@ -2158,7 +2167,6 @@ typedef enum : NSUInteger { MediaGallery *mediaGallery = [[MediaGallery alloc] initWithThread:self.thread - uiDatabaseConnection:self.uiDatabaseConnection options:MediaGalleryOptionSliderEnabled | MediaGalleryOptionShowAllMediaButton]; [mediaGallery presentDetailViewFromViewController:self mediaAttachment:attachmentStream replacingView:imageView]; @@ -2181,7 +2189,6 @@ typedef enum : NSUInteger { MediaGallery *mediaGallery = [[MediaGallery alloc] initWithThread:self.thread - uiDatabaseConnection:self.uiDatabaseConnection options:MediaGalleryOptionSliderEnabled | MediaGalleryOptionShowAllMediaButton]; [mediaGallery presentDetailViewFromViewController:self mediaAttachment:attachmentStream replacingView:imageView]; @@ -2358,12 +2365,13 @@ typedef enum : NSUInteger { OWSAssertDebug([conversationItem.interaction isKindOfClass:[TSMessage class]]); TSMessage *message = (TSMessage *)conversationItem.interaction; - MessageDetailViewController *view = + MessageDetailViewController *detailVC = [[MessageDetailViewController alloc] initWithViewItem:conversationItem message:message thread:self.thread mode:MessageMetadataViewModeFocusOnMetadata]; - [self.navigationController pushViewController:view animated:YES]; + detailVC.delegate = self; + [self.navigationController pushViewController:detailVC animated:YES]; } - (void)populateReplyForViewItem:(id)conversationItem diff --git a/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift b/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift index 09a672925..95f3c10cc 100644 --- a/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift +++ b/Signal/src/ViewControllers/HomeView/ConversationSearchViewController.swift @@ -73,8 +73,8 @@ class ConversationSearchViewController: UITableViewController, BlockListCacheDel tableView.register(ContactTableViewCell.self, forCellReuseIdentifier: ContactTableViewCell.reuseIdentifier()) NotificationCenter.default.addObserver(self, - selector: #selector(yapDatabaseModified), - name: NSNotification.Name.YapDatabaseModified, + selector: #selector(uiDatabaseModified), + name: .OWSUIDatabaseConnectionDidUpdate, object: OWSPrimaryStorage.shared().dbNotificationObject) NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), @@ -101,7 +101,7 @@ class ConversationSearchViewController: UITableViewController, BlockListCacheDel NotificationCenter.default.removeObserver(self) } - @objc internal func yapDatabaseModified(notification: NSNotification) { + @objc internal func uiDatabaseModified(notification: NSNotification) { AssertIsOnMainThread() refreshSearchResults() diff --git a/Signal/src/ViewControllers/MediaGalleryViewController.swift b/Signal/src/ViewControllers/MediaGalleryViewController.swift index 540f8aeab..984678ce2 100644 --- a/Signal/src/ViewControllers/MediaGalleryViewController.swift +++ b/Signal/src/ViewControllers/MediaGalleryViewController.swift @@ -229,11 +229,11 @@ protocol MediaGalleryDataSource: class { func showAllMedia(focusedItem: MediaGalleryItem) func dismissMediaDetailViewController(_ mediaDetailViewController: MediaPageViewController, animated isAnimated: Bool, completion: (() -> Void)?) - func delete(items: [MediaGalleryItem], initiatedBy: MediaGalleryDataSourceDelegate) + func delete(items: [MediaGalleryItem], initiatedBy: AnyObject) } protocol MediaGalleryDataSourceDelegate: class { - func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem], initiatedBy: MediaGalleryDataSourceDelegate) + func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem], initiatedBy: AnyObject) func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, deletedSections: IndexSet, deletedItems: [IndexPath]) } @@ -318,7 +318,10 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel private var pageViewController: MediaPageViewController? - private let uiDatabaseConnection: YapDatabaseConnection + private var uiDatabaseConnection: YapDatabaseConnection { + return OWSPrimaryStorage.shared().uiDatabaseConnection + } + private let editingDatabaseConnection: YapDatabaseConnection private let mediaGalleryFinder: OWSMediaGalleryFinder @@ -334,16 +337,19 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel } @objc - init(thread: TSThread, uiDatabaseConnection: YapDatabaseConnection, options: MediaGalleryOption = []) { + init(thread: TSThread, options: MediaGalleryOption = []) { self.thread = thread - assert(uiDatabaseConnection.isInLongLivedReadTransaction()) - self.uiDatabaseConnection = uiDatabaseConnection self.editingDatabaseConnection = OWSPrimaryStorage.shared().newDatabaseConnection() self.options = options self.mediaGalleryFinder = OWSMediaGalleryFinder(thread: thread) super.init() + + NotificationCenter.default.addObserver(self, + selector: #selector(uiDatabaseDidUpdate), + name: .OWSUIDatabaseConnectionDidUpdate, + object: OWSPrimaryStorage.shared().dbNotificationObject) } // MARK: Present/Dismiss @@ -709,7 +715,70 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel ] } - // MARK: MediaGalleryDataSource + // MARK: - Database Notifications + + @objc + func uiDatabaseDidUpdate(notification: Notification) { + guard let notifications = notification.userInfo?[OWSUIDatabaseConnectionNotificationsKey] as? [Notification] else { + owsFailDebug("notifications was unexpectedly nil") + return + } + + guard mediaGalleryFinder.hasMediaChanges(in: notifications, dbConnection: uiDatabaseConnection) else { + Logger.verbose("no changes for thread: \(thread)") + return + } + + let rowChanges = extractRowChanges(notifications: notifications) + assert(rowChanges.count > 0) + + process(rowChanges: rowChanges) + } + + func extractRowChanges(notifications: [Notification]) -> [YapDatabaseViewRowChange] { + return notifications.flatMap { notification -> [YapDatabaseViewRowChange] in + guard let userInfo = notification.userInfo else { + owsFailDebug("userInfo was unexpectedly nil") + return [] + } + + guard let extensionChanges = userInfo["extensions"] as? [AnyHashable: Any] else { + owsFailDebug("extensionChanges was unexpectedly nil") + return [] + } + + guard let galleryData = extensionChanges[OWSMediaGalleryFinder.databaseExtensionName()] as? [AnyHashable: Any] else { + owsFailDebug("galleryData was unexpectedly nil") + return [] + } + + guard let galleryChanges = galleryData["changes"] as? [Any] else { + owsFailDebug("gallerlyChanges was unexpectedly nil") + return [] + } + + return galleryChanges.compactMap { $0 as? YapDatabaseViewRowChange } + } + } + + func process(rowChanges: [YapDatabaseViewRowChange]) { + let deleteChanges = rowChanges.filter { $0.type == .delete } + + let deletedItems: [MediaGalleryItem] = deleteChanges.compactMap { (deleteChange: YapDatabaseViewRowChange) -> MediaGalleryItem? in + guard let deletedItem = self.galleryItems.first(where: { galleryItem in + galleryItem.attachmentStream.uniqueId == deleteChange.collectionKey.key + }) else { + Logger.debug("deletedItem was never loaded - no need to remove.") + return nil + } + + return deletedItem + } + + self.delete(items: deletedItems, initiatedBy: self) + } + + // MARK: - MediaGalleryDataSource lazy var mediaTileViewController: MediaTileViewController = { let vc = MediaTileViewController(mediaGalleryDataSource: self, uiDatabaseConnection: self.uiDatabaseConnection) @@ -777,6 +846,10 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel } } + enum MediaGalleryError: Error { + case itemNoLongerExists + } + func ensureGalleryItemsLoaded(_ direction: GalleryDirection, item: MediaGalleryItem, amount: UInt, completion: ((IndexSet, [IndexPath]) -> Void)? = nil ) { var galleryItems: [MediaGalleryItem] = self.galleryItems @@ -786,92 +859,102 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel var newGalleryItems: [MediaGalleryItem] = [] var newDates: [GalleryDate] = [] - Bench(title: "fetching gallery items") { - self.uiDatabaseConnection.read { transaction in - - let initialIndex: Int = Int(self.mediaGalleryFinder.mediaIndex(attachment: item.attachmentStream, transaction: transaction)) - let mediaCount: Int = Int(self.mediaGalleryFinder.mediaCount(transaction: transaction)) - - let requestRange: Range = { () -> Range in - let range: Range = { () -> Range in - switch direction { - case .around: - // To keep it simple, this isn't exactly *amount* sized if `message` window overlaps the end or - // beginning of the view. Still, we have sufficient buffer to fetch more as the user swipes. - let start: Int = initialIndex - Int(amount) / 2 - let end: Int = initialIndex + Int(amount) / 2 - - return start.. = { () -> Range in + let range: Range = { () -> Range in + switch direction { + case .around: + // To keep it simple, this isn't exactly *amount* sized if `message` window overlaps the end or + // beginning of the view. Still, we have sufficient buffer to fetch more as the user swipes. + let start: Int = initialIndex - Int(amount) / 2 + let end: Int = initialIndex + Int(amount) / 2 + + return start.. (requestSet.count / 2) - // ...but we always fulfill even small requests if we're getting just the tail end of a gallery. - let isFetchingEdgeOfGallery = (self.fetchedIndexSet.count - unfetchedSet.count) < requestSet.count + let requestSet = IndexSet(integersIn: requestRange) + guard !self.fetchedIndexSet.contains(integersIn: requestSet) else { + Logger.debug("all requested messages have already been loaded.") + return + } - guard isSubstantialRequest || isFetchingEdgeOfGallery else { - Logger.debug("ignoring small fetch request: \(unfetchedSet.count)") - return - } + let unfetchedSet = requestSet.subtracting(self.fetchedIndexSet) - Logger.debug("fetching set: \(unfetchedSet)") - let nsRange: NSRange = NSRange(location: unfetchedSet.min()!, length: unfetchedSet.count) - self.mediaGalleryFinder.enumerateMediaAttachments(range: nsRange, transaction: transaction) { (attachment: TSAttachment) in + // For perf we only want to fetch a substantially full batch... + let isSubstantialRequest = unfetchedSet.count > (requestSet.count / 2) + // ...but we always fulfill even small requests if we're getting just the tail end of a gallery. + let isFetchingEdgeOfGallery = (self.fetchedIndexSet.count - unfetchedSet.count) < requestSet.count - guard !self.deletedAttachments.contains(attachment) else { - Logger.debug("skipping \(attachment) which has been deleted.") + guard isSubstantialRequest || isFetchingEdgeOfGallery else { + Logger.debug("ignoring small fetch request: \(unfetchedSet.count)") return } - guard let item: MediaGalleryItem = self.buildGalleryItem(attachment: attachment, transaction: transaction) else { - owsFailDebug("unexpectedly failed to buildGalleryItem") - return - } + Logger.debug("fetching set: \(unfetchedSet)") + let nsRange: NSRange = NSRange(location: unfetchedSet.min()!, length: unfetchedSet.count) + self.mediaGalleryFinder.enumerateMediaAttachments(range: nsRange, transaction: transaction) { (attachment: TSAttachment) in - let date = item.galleryDate + guard !self.deletedAttachments.contains(attachment) else { + Logger.debug("skipping \(attachment) which has been deleted.") + return + } - galleryItems.append(item) - if sections[date] != nil { - sections[date]!.append(item) + guard let item: MediaGalleryItem = self.buildGalleryItem(attachment: attachment, transaction: transaction) else { + owsFailDebug("unexpectedly failed to buildGalleryItem") + return + } - // so we can update collectionView - newGalleryItems.append(item) - } else { - sectionDates.append(date) - sections[date] = [item] + let date = item.galleryDate - // so we can update collectionView - newDates.append(date) - newGalleryItems.append(item) + galleryItems.append(item) + if sections[date] != nil { + sections[date]!.append(item) + + // so we can update collectionView + newGalleryItems.append(item) + } else { + sectionDates.append(date) + sections[date] = [item] + + // so we can update collectionView + newDates.append(date) + newGalleryItems.append(item) + } } - } - self.fetchedIndexSet = self.fetchedIndexSet.union(unfetchedSet) - self.hasFetchedOldest = self.fetchedIndexSet.min() == 0 - self.hasFetchedMostRecent = self.fetchedIndexSet.max() == mediaCount - 1 + self.fetchedIndexSet = self.fetchedIndexSet.union(unfetchedSet) + self.hasFetchedOldest = self.fetchedIndexSet.min() == 0 + self.hasFetchedMostRecent = self.fetchedIndexSet.max() == mediaCount - 1 + } } + } catch MediaGalleryError.itemNoLongerExists { + Logger.debug("Ignoring reload, since item no longer exists.") + return + } catch { + owsFailDebug("unexpected error: \(error)") + return } // TODO only sort if changed @@ -919,7 +1002,7 @@ class MediaGallery: NSObject, MediaGalleryDataSource, MediaTileViewControllerDel dataSourceDelegates.append(Weak(value: dataSourceDelegate)) } - func delete(items: [MediaGalleryItem], initiatedBy: MediaGalleryDataSourceDelegate) { + func delete(items: [MediaGalleryItem], initiatedBy: AnyObject) { AssertIsOnMainThread() Logger.info("with items: \(items.map { ($0.attachmentStream, $0.message.timestamp) })") diff --git a/Signal/src/ViewControllers/MediaPageViewController.swift b/Signal/src/ViewControllers/MediaPageViewController.swift index 76d8960ac..412eb652e 100644 --- a/Signal/src/ViewControllers/MediaPageViewController.swift +++ b/Signal/src/ViewControllers/MediaPageViewController.swift @@ -401,11 +401,11 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou // MARK: MediaGalleryDataSourceDelegate - func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem], initiatedBy: MediaGalleryDataSourceDelegate) { + func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem], initiatedBy: AnyObject) { Logger.debug("") guard let currentItem = self.currentItem else { - owsFailDebug("currentItem was unexpectedly nil") + owsFailDebug("currentItem was unexpectedly nil") return } diff --git a/Signal/src/ViewControllers/MediaTileViewController.swift b/Signal/src/ViewControllers/MediaTileViewController.swift index 9b4006bb5..357b8912f 100644 --- a/Signal/src/ViewControllers/MediaTileViewController.swift +++ b/Signal/src/ViewControllers/MediaTileViewController.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // import Foundation @@ -610,7 +610,7 @@ public class MediaTileViewController: UICollectionViewController, MediaGalleryDa // MARK: MediaGalleryDataSourceDelegate - func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem], initiatedBy: MediaGalleryDataSourceDelegate) { + func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem], initiatedBy: AnyObject) { Logger.debug("") guard let collectionView = self.collectionView else { diff --git a/Signal/src/ViewControllers/MessageDetailViewController.swift b/Signal/src/ViewControllers/MessageDetailViewController.swift index 251269d44..9618576e2 100644 --- a/Signal/src/ViewControllers/MessageDetailViewController.swift +++ b/Signal/src/ViewControllers/MessageDetailViewController.swift @@ -12,8 +12,17 @@ enum MessageMetadataViewMode: UInt { case focusOnMetadata } +@objc +protocol MessageDetailViewDelegate: AnyObject { + func detailViewMessageWasDeleted(_ messageDetailViewController: MessageDetailViewController) +} + +@objc class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDelegate, OWSMessageBubbleViewDelegate, ContactShareViewHelperDelegate { + @objc + weak var delegate: MessageDetailViewDelegate? + // MARK: Properties let uiDatabaseConnection: YapDatabaseConnection @@ -67,7 +76,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele self.viewItem = viewItem self.message = message self.mode = mode - self.uiDatabaseConnection = OWSPrimaryStorage.shared().newDatabaseConnection() + self.uiDatabaseConnection = OWSPrimaryStorage.shared().uiDatabaseConnection self.conversationStyle = ConversationStyle(thread: thread) super.init(nibName: nil, bundle: nil) @@ -80,8 +89,13 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele self.contactShareViewHelper = ContactShareViewHelper(contactsManager: contactsManager) contactShareViewHelper.delegate = self - self.uiDatabaseConnection.beginLongLivedReadTransaction() - updateDBConnectionAndMessageToLatest() + do { + try updateDBConnectionAndMessageToLatest() + } catch DetailViewError.messageWasDeleted { + self.delegate?.detailViewMessageWasDeleted(self) + } catch { + owsFailDebug("unexpected error") + } self.conversationStyle.viewWidth = view.width() @@ -93,8 +107,8 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele self.view.layoutIfNeeded() NotificationCenter.default.addObserver(self, - selector: #selector(yapDatabaseModified), - name: NSNotification.Name.YapDatabaseModified, + selector: #selector(uiDatabaseDidUpdate), + name: .OWSUIDatabaseConnectionDidUpdate, object: OWSPrimaryStorage.shared().dbNotificationObject) } @@ -524,19 +538,23 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele // MARK: - Actions + enum DetailViewError: Error { + case messageWasDeleted + } + // This method should be called after self.databaseConnection.beginLongLivedReadTransaction(). - private func updateDBConnectionAndMessageToLatest() { + private func updateDBConnectionAndMessageToLatest() throws { AssertIsOnMainThread() - self.uiDatabaseConnection.read { transaction in + try self.uiDatabaseConnection.read { transaction in guard let uniqueId = self.message.uniqueId else { Logger.error("Message is missing uniqueId.") return } guard let newMessage = TSInteraction.fetch(uniqueId: uniqueId, transaction: transaction) as? TSMessage else { - Logger.error("Couldn't reload message.") - return + Logger.error("Message was deleted") + throw DetailViewError.messageWasDeleted } self.message = newMessage self.attachment = self.fetchAttachment(transaction: transaction) @@ -544,20 +562,25 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele } } - @objc internal func yapDatabaseModified(notification: NSNotification) { + @objc internal func uiDatabaseDidUpdate(notification: NSNotification) { AssertIsOnMainThread() guard !wasDeleted else { - // Item was deleted. Don't bother re-rendering, it will fail and we'll soon be dismissed. + // Item was deleted in the tile view gallery. + // Don't bother re-rendering, it will fail and we'll soon be dismissed. return } - let notifications = self.uiDatabaseConnection.beginLongLivedReadTransaction() + guard let notifications = notification.userInfo?[OWSUIDatabaseConnectionNotificationsKey] as? [Notification] else { + owsFailDebug("notifications was unexpectedly nil") + return + } guard let uniqueId = self.message.uniqueId else { Logger.error("Message is missing uniqueId.") return } + guard self.uiDatabaseConnection.hasChange(forKey: uniqueId, inCollection: TSInteraction.collection(), in: notifications) else { @@ -565,7 +588,16 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele return } - updateDBConnectionAndMessageToLatest() + do { + try updateDBConnectionAndMessageToLatest() + } catch DetailViewError.messageWasDeleted { + DispatchQueue.main.async { + self.delegate?.detailViewMessageWasDeleted(self) + } + return + } catch { + owsFailDebug("unexpected error: \(error)") + } updateContent() } @@ -616,14 +648,14 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele // MARK: OWSMessageBubbleViewDelegate func didTapImageViewItem(_ viewItem: ConversationViewItem, attachmentStream: TSAttachmentStream, imageView: UIView) { - let mediaGallery = MediaGallery(thread: self.thread, uiDatabaseConnection: self.uiDatabaseConnection) + let mediaGallery = MediaGallery(thread: self.thread) mediaGallery.addDataSourceDelegate(self) mediaGallery.presentDetailView(fromViewController: self, mediaAttachment: attachmentStream, replacingView: imageView) } func didTapVideoViewItem(_ viewItem: ConversationViewItem, attachmentStream: TSAttachmentStream, imageView: UIView) { - let mediaGallery = MediaGallery(thread: self.thread, uiDatabaseConnection: self.uiDatabaseConnection) + let mediaGallery = MediaGallery(thread: self.thread) mediaGallery.addDataSourceDelegate(self) mediaGallery.presentDetailView(fromViewController: self, mediaAttachment: attachmentStream, replacingView: imageView) @@ -732,7 +764,7 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele // MediaGalleryDataSourceDelegate - func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem], initiatedBy: MediaGalleryDataSourceDelegate) { + func mediaGalleryDataSource(_ mediaGalleryDataSource: MediaGalleryDataSource, willDelete items: [MediaGalleryItem], initiatedBy: AnyObject) { Logger.info("") guard (items.map({ $0.message }) == [self.message]) else { diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m index 4df03214e..a85f43e42 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m @@ -1318,7 +1318,6 @@ const CGFloat kIconViewLength = 24; OWSLogDebug(@""); MediaGallery *mediaGallery = [[MediaGallery alloc] initWithThread:self.thread - uiDatabaseConnection:self.uiDatabaseConnection options:MediaGalleryOptionSliderEnabled]; self.mediaGallery = mediaGallery; diff --git a/SignalMessaging/utils/Bench.swift b/SignalMessaging/utils/Bench.swift index cf79debb5..34afa6385 100644 --- a/SignalMessaging/utils/Bench.swift +++ b/SignalMessaging/utils/Bench.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // import Foundation @@ -9,7 +9,10 @@ import Foundation /// /// BenchAsync(title: "my benchmark") { completeBenchmark in /// foo { +/// // consider benchmarking of "foo" complete /// completeBenchmark() +/// +/// // call any completion handler foo might have /// fooCompletion() /// } /// } @@ -30,6 +33,21 @@ public func Bench(title: String, block: () -> Void) { } } +public func Bench(title: String, block: () throws -> Void) throws { + var thrownError: Error? + BenchAsync(title: title) { finish in + do { + try block() + } catch { + thrownError = error + } + finish() + } + if let errorToRethrow = thrownError { + throw errorToRethrow + } +} + /// When it's not convenient to retain the event completion handler, e.g. when the measured event /// crosses multiple classes, you can use the BenchEvent tools /// diff --git a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.h b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.h index 5fcbb6108..c583b33ac 100644 --- a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.h +++ b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @@ -7,7 +7,10 @@ NS_ASSUME_NONNULL_BEGIN @class OWSStorage; @class TSAttachment; @class TSThread; +@class YapDatabaseAutoViewTransaction; +@class YapDatabaseConnection; @class YapDatabaseReadTransaction; +@class YapDatabaseViewRowChange; @interface OWSMediaGalleryFinder : NSObject @@ -19,8 +22,8 @@ NS_ASSUME_NONNULL_BEGIN - (NSUInteger)mediaCountWithTransaction:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(mediaCount(transaction:)); // The ordinal position of an attachment within a thread's media gallery -- (NSUInteger)mediaIndexForAttachment:(TSAttachment *)attachment - transaction:(YapDatabaseReadTransaction *)transaction +- (nullable NSNumber *)mediaIndexForAttachment:(TSAttachment *)attachment + transaction:(YapDatabaseReadTransaction *)transaction NS_SWIFT_NAME(mediaIndex(attachment:transaction:)); - (nullable TSAttachment *)oldestMediaAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction @@ -33,8 +36,14 @@ NS_ASSUME_NONNULL_BEGIN block:(void (^)(TSAttachment *))attachmentBlock NS_SWIFT_NAME(enumerateMediaAttachments(range:transaction:block:)); +- (BOOL)hasMediaChangesInNotifications:(NSArray *)notifications + dbConnection:(YapDatabaseConnection *)dbConnection; + #pragma mark - Extension registration +@property (nonatomic, readonly) NSString *mediaGroup; +- (YapDatabaseAutoViewTransaction *)galleryExtensionWithTransaction:(YapDatabaseReadTransaction *)transaction + NS_SWIFT_NAME(galleryExtension(transaction:)); + (NSString *)databaseExtensionName; + (void)asyncRegisterDatabaseExtensionsWithPrimaryStorage:(OWSStorage *)storage; diff --git a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m index 3af82d088..7a22c368a 100644 --- a/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m +++ b/SignalServiceKit/src/Storage/OWSMediaGalleryFinder.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import "OWSMediaGalleryFinder.h" @@ -43,7 +43,8 @@ static NSString *const OWSMediaGalleryFinderExtensionName = @"OWSMediaGalleryFin return [[self galleryExtensionWithTransaction:transaction] numberOfItemsInGroup:self.mediaGroup]; } -- (NSUInteger)mediaIndexForAttachment:(TSAttachment *)attachment transaction:(YapDatabaseReadTransaction *)transaction +- (nullable NSNumber *)mediaIndexForAttachment:(TSAttachment *)attachment + transaction:(YapDatabaseReadTransaction *)transaction { NSString *groupId; NSUInteger index; @@ -53,10 +54,13 @@ static NSString *const OWSMediaGalleryFinderExtensionName = @"OWSMediaGalleryFin forKey:attachment.uniqueId inCollection:[TSAttachment collection]]; - OWSAssertDebug(wasFound); + if (!wasFound) { + return nil; + } + OWSAssertDebug([self.mediaGroup isEqual:groupId]); - return index; + return @(index); } - (nullable TSAttachment *)oldestMediaAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction @@ -88,6 +92,15 @@ static NSString *const OWSMediaGalleryFinderExtensionName = @"OWSMediaGalleryFin }]; } +- (BOOL)hasMediaChangesInNotifications:(NSArray *)notifications + dbConnection:(YapDatabaseConnection *)dbConnection +{ + YapDatabaseAutoViewConnection *extConnection = [dbConnection ext:OWSMediaGalleryFinderExtensionName]; + OWSAssert(extConnection); + + return [extConnection hasChangesForGroup:self.mediaGroup inNotifications:notifications]; +} + #pragma mark - Util - (YapDatabaseAutoViewTransaction *)galleryExtensionWithTransaction:(YapDatabaseReadTransaction *)transaction diff --git a/SignalServiceKit/src/Util/YapDatabase+Promise.swift b/SignalServiceKit/src/Util/YapDatabase+Promise.swift index 2fe3351a2..89d7029ed 100644 --- a/SignalServiceKit/src/Util/YapDatabase+Promise.swift +++ b/SignalServiceKit/src/Util/YapDatabase+Promise.swift @@ -17,4 +17,36 @@ public extension YapDatabaseConnection { self.asyncReadWrite(block, completionBlock: resolver.fulfill) } } + + func read(_ block: @escaping (YapDatabaseReadTransaction) throws -> Void) throws { + var errorToRaise: Error? = nil + + read { transaction in + do { + try block(transaction) + } catch { + errorToRaise = error + } + } + + if let errorToRaise = errorToRaise { + throw errorToRaise + } + } + + func readWrite(_ block: @escaping (YapDatabaseReadWriteTransaction) throws -> Void) throws { + var errorToRaise: Error? = nil + + readWrite { transaction in + do { + try block(transaction) + } catch { + errorToRaise = error + } + } + + if let errorToRaise = errorToRaise { + throw errorToRaise + } + } }