pop view if message is deleted

- use global ui database connection
This commit is contained in:
Michael Kirk 2019-02-20 12:44:30 -07:00
parent 1afc251401
commit fabd3996c2
11 changed files with 305 additions and 111 deletions

View File

@ -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<ConversationViewItem>)conversationItem

View File

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

View File

@ -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
do {
try Bench(title: "fetching gallery items") {
try self.uiDatabaseConnection.read { transaction in
guard let index = self.mediaGalleryFinder.mediaIndex(attachment: item.attachmentStream, transaction: transaction) else {
throw MediaGalleryError.itemNoLongerExists
}
let initialIndex: Int = index.intValue
let mediaCount: Int = Int(self.mediaGalleryFinder.mediaCount(transaction: transaction))
let initialIndex: Int = Int(self.mediaGalleryFinder.mediaIndex(attachment: item.attachmentStream, transaction: transaction))
let mediaCount: Int = Int(self.mediaGalleryFinder.mediaCount(transaction: transaction))
let requestRange: Range<Int> = { () -> Range<Int> in
let range: Range<Int> = { () -> Range<Int> 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
let requestRange: Range<Int> = { () -> Range<Int> in
let range: Range<Int> = { () -> Range<Int> 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..<end
case .before:
let start: Int = initialIndex - Int(amount)
let end: Int = initialIndex
return start..<end
case .before:
let start: Int = initialIndex - Int(amount)
let end: Int = initialIndex
return start..<end
case .after:
let start: Int = initialIndex
let end: Int = initialIndex + Int(amount)
return start..<end
case .after:
let start: Int = initialIndex
let end: Int = initialIndex + Int(amount)
return start..<end
}
}()
return start..<end
}
return range.clamped(to: 0..<mediaCount)
}()
return range.clamped(to: 0..<mediaCount)
}()
let requestSet = IndexSet(integersIn: requestRange)
guard !self.fetchedIndexSet.contains(integersIn: requestSet) else {
Logger.debug("all requested messages have already been loaded.")
return
}
let unfetchedSet = requestSet.subtracting(self.fetchedIndexSet)
// 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 isSubstantialRequest || isFetchingEdgeOfGallery else {
Logger.debug("ignoring small fetch request: \(unfetchedSet.count)")
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
guard !self.deletedAttachments.contains(attachment) else {
Logger.debug("skipping \(attachment) which has been deleted.")
let requestSet = IndexSet(integersIn: requestRange)
guard !self.fetchedIndexSet.contains(integersIn: requestSet) else {
Logger.debug("all requested messages have already been loaded.")
return
}
guard let item: MediaGalleryItem = self.buildGalleryItem(attachment: attachment, transaction: transaction) else {
owsFailDebug("unexpectedly failed to buildGalleryItem")
let unfetchedSet = requestSet.subtracting(self.fetchedIndexSet)
// 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 isSubstantialRequest || isFetchingEdgeOfGallery else {
Logger.debug("ignoring small fetch request: \(unfetchedSet.count)")
return
}
let date = item.galleryDate
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
galleryItems.append(item)
if sections[date] != nil {
sections[date]!.append(item)
guard !self.deletedAttachments.contains(attachment) else {
Logger.debug("skipping \(attachment) which has been deleted.")
return
}
// so we can update collectionView
newGalleryItems.append(item)
} else {
sectionDates.append(date)
sections[date] = [item]
guard let item: MediaGalleryItem = self.buildGalleryItem(attachment: attachment, transaction: transaction) else {
owsFailDebug("unexpectedly failed to buildGalleryItem")
return
}
// so we can update collectionView
newDates.append(date)
newGalleryItems.append(item)
let date = item.galleryDate
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) })")

View File

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

View File

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

View File

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

View File

@ -1318,7 +1318,6 @@ const CGFloat kIconViewLength = 24;
OWSLogDebug(@"");
MediaGallery *mediaGallery = [[MediaGallery alloc] initWithThread:self.thread
uiDatabaseConnection:self.uiDatabaseConnection
options:MediaGalleryOptionSliderEnabled];
self.mediaGallery = mediaGallery;

View File

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

View File

@ -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<NSNotification *> *)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;

View File

@ -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<NSNotification *> *)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

View File

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