mirror of https://github.com/oxen-io/session-ios
Let users share imported files to a thread or contact of their choice.
// FREEBIEpull/1/head
parent
3c7574a908
commit
6e36ce97a5
@ -0,0 +1,21 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
@class TSThread;
|
||||
|
||||
@protocol SelectThreadViewControllerDelegate <NSObject>
|
||||
|
||||
- (void)threadWasSelected:(TSThread *)thread;
|
||||
|
||||
- (BOOL)canSelectBlockedContact;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface SelectThreadViewController : UIViewController
|
||||
|
||||
@property (nonatomic, weak) id<SelectThreadViewControllerDelegate> delegate;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,428 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SelectThreadViewController.h"
|
||||
#import "BlockListUIUtils.h"
|
||||
#import "ContactTableViewCell.h"
|
||||
#import "Environment.h"
|
||||
#import "InboxTableViewCell.h"
|
||||
#import "OWSContactsManager.h"
|
||||
#import "OWSContactsSearcher.h"
|
||||
#import "OWSTableViewController.h"
|
||||
#import "ThreadViewHelper.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "UIFont+OWS.h"
|
||||
#import "UIView+OWS.h"
|
||||
#import <SignalServiceKit/OWSBlockingManager.h>
|
||||
#import <SignalServiceKit/TSAccountManager.h>
|
||||
#import <SignalServiceKit/TSContactThread.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SelectThreadViewController () <OWSTableViewControllerDelegate, ThreadViewHelperDelegate, UISearchBarDelegate>
|
||||
|
||||
@property (nonatomic, readonly) OWSBlockingManager *blockingManager;
|
||||
@property (nonatomic) NSSet<NSString *> *blockedPhoneNumberSet;
|
||||
|
||||
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
|
||||
@property (nonatomic) NSArray<Contact *> *contacts;
|
||||
|
||||
@property (nonatomic, readonly) ThreadViewHelper *threadViewHelper;
|
||||
|
||||
@property (nonatomic, readonly) OWSTableViewController *tableViewController;
|
||||
@property (nonatomic, readonly) UISearchBar *searchBar;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation SelectThreadViewController
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
|
||||
self.navigationItem.leftBarButtonItem =
|
||||
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemStop
|
||||
target:self
|
||||
action:@selector(dismissPressed:)];
|
||||
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
_blockingManager = [OWSBlockingManager sharedManager];
|
||||
_blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]];
|
||||
_contactsManager = [Environment getCurrent].contactsManager;
|
||||
self.contacts = [self filteredContacts];
|
||||
_threadViewHelper = [ThreadViewHelper new];
|
||||
_threadViewHelper.delegate = self;
|
||||
|
||||
[self createViews];
|
||||
|
||||
[self addNotificationListeners];
|
||||
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
}
|
||||
|
||||
- (void)addNotificationListeners
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(blockedPhoneNumbersDidChange:)
|
||||
name:kNSNotificationName_BlockedPhoneNumbersDidChange
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(signalRecipientsDidChange:)
|
||||
name:OWSContactsManagerSignalRecipientsDidChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)createViews
|
||||
{
|
||||
// Table
|
||||
_tableViewController = [OWSTableViewController new];
|
||||
_tableViewController.delegate = self;
|
||||
_tableViewController.contents = [OWSTableContents new];
|
||||
[self.view addSubview:self.tableViewController.view];
|
||||
[_tableViewController.view autoPinWidthToSuperview];
|
||||
[_tableViewController.view autoPinToTopLayoutGuideOfViewController:self withInset:0];
|
||||
[_tableViewController.view autoPinEdgeToSuperviewEdge:ALEdgeBottom];
|
||||
|
||||
// Search
|
||||
UISearchBar *searchBar = [UISearchBar new];
|
||||
_searchBar = searchBar;
|
||||
searchBar.searchBarStyle = UISearchBarStyleProminent;
|
||||
searchBar.delegate = self;
|
||||
searchBar.placeholder = NSLocalizedString(@"SEARCH_BYNAMEORNUMBER_PLACEHOLDER_TEXT", @"");
|
||||
[searchBar sizeToFit];
|
||||
_tableViewController.tableView.tableHeaderView = searchBar;
|
||||
}
|
||||
|
||||
#pragma mark - UISearchBarDelegate
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
||||
{
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
- (void)searchBarResultsListButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope
|
||||
{
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (void)updateTableContents
|
||||
{
|
||||
__weak SelectThreadViewController *weakSelf = self;
|
||||
OWSTableContents *contents = [OWSTableContents new];
|
||||
OWSTableSection *section = [OWSTableSection new];
|
||||
|
||||
// Threads
|
||||
for (TSThread *thread in [self filteredThreadsWithSearchText]) {
|
||||
[section addItem:[OWSTableItem itemWithCustomCellBlock:^{
|
||||
SelectThreadViewController *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return (InboxTableViewCell *)nil;
|
||||
}
|
||||
|
||||
InboxTableViewCell *cell = [InboxTableViewCell inboxTableViewCell];
|
||||
|
||||
[cell configureWithThread:thread
|
||||
contactsManager:strongSelf.contactsManager
|
||||
blockedPhoneNumberSet:strongSelf.blockedPhoneNumberSet];
|
||||
|
||||
return cell;
|
||||
}
|
||||
customRowHeight:[InboxTableViewCell rowHeight]
|
||||
actionBlock:^{
|
||||
[weakSelf.delegate threadWasSelected:thread];
|
||||
}]];
|
||||
}
|
||||
|
||||
// Contacts
|
||||
NSArray<Contact *> *filteredContacts = [self filteredContactsWithSearchText];
|
||||
for (Contact *contact in filteredContacts) {
|
||||
[section addItem:[OWSTableItem itemWithCustomCellBlock:^{
|
||||
SelectThreadViewController *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return (InboxTableViewCell *)nil;
|
||||
}
|
||||
|
||||
// To be consistent with the threads (above), we use InboxTableViewCell
|
||||
// instead of ContactTableViewCell to present contacts.
|
||||
InboxTableViewCell *cell = [InboxTableViewCell inboxTableViewCell];
|
||||
|
||||
// TODO: Use ContactAccount.
|
||||
NSString *recipientId = contact.textSecureIdentifiers.firstObject;
|
||||
BOOL isBlocked = [strongSelf isContactBlocked:contact];
|
||||
|
||||
[cell configureWithContact:contact
|
||||
recipientId:recipientId
|
||||
contactsManager:strongSelf.contactsManager
|
||||
isBlocked:isBlocked];
|
||||
|
||||
return cell;
|
||||
}
|
||||
customRowHeight:[InboxTableViewCell rowHeight]
|
||||
actionBlock:^{
|
||||
[weakSelf contactWasSelected:contact];
|
||||
}]];
|
||||
}
|
||||
|
||||
if (section.itemCount < 1) {
|
||||
[section addItem:[OWSTableItem itemWithCustomCellBlock:^{
|
||||
UITableViewCell *cell = [UITableViewCell new];
|
||||
cell.textLabel.text = NSLocalizedString(
|
||||
@"SETTINGS_BLOCK_LIST_NO_CONTACTS", @"A label that indicates the user has no Signal contacts.");
|
||||
cell.textLabel.font = [UIFont ows_regularFontWithSize:15.f];
|
||||
cell.textLabel.textColor = [UIColor colorWithWhite:0.5f alpha:1.f];
|
||||
cell.textLabel.textAlignment = NSTextAlignmentCenter;
|
||||
return cell;
|
||||
}
|
||||
actionBlock:nil]];
|
||||
}
|
||||
[contents addSection:section];
|
||||
|
||||
self.tableViewController.contents = contents;
|
||||
}
|
||||
|
||||
- (void)contactWasSelected:(Contact *)contact
|
||||
{
|
||||
OWSAssert(contact);
|
||||
OWSAssert(self.delegate);
|
||||
|
||||
// TODO: Use ContactAccount.
|
||||
NSString *recipientId = contact.textSecureIdentifiers.firstObject;
|
||||
|
||||
if ([self isRecipientIdBlocked:recipientId] &&
|
||||
![self.delegate canSelectBlockedContact]) {
|
||||
|
||||
__weak SelectThreadViewController *weakSelf = self;
|
||||
[BlockListUIUtils showUnblockContactActionSheet:contact
|
||||
fromViewController:self
|
||||
blockingManager:self.blockingManager
|
||||
contactsManager:self.contactsManager
|
||||
completionBlock:^(BOOL isBlocked) {
|
||||
if (!isBlocked) {
|
||||
[weakSelf contactWasSelected:contact];
|
||||
}
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
__block TSThread *thread = nil;
|
||||
[[TSStorageManager sharedManager].dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
thread = [TSContactThread getOrCreateThreadWithContactId:recipientId transaction:transaction];
|
||||
}];
|
||||
OWSAssert(thread);
|
||||
|
||||
[self.delegate threadWasSelected:thread];
|
||||
}
|
||||
|
||||
#pragma mark - Filter
|
||||
|
||||
- (NSArray<TSThread *> *)filteredThreadsWithSearchText
|
||||
{
|
||||
NSArray<TSThread *> *threads = self.threadViewHelper.threads;
|
||||
|
||||
NSString *searchTerm =
|
||||
[[self.searchBar text] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
|
||||
if ([searchTerm isEqualToString:@""]) {
|
||||
return threads;
|
||||
}
|
||||
|
||||
NSString *formattedNumber = [PhoneNumber removeFormattingCharacters:searchTerm];
|
||||
|
||||
NSMutableArray *result = [NSMutableArray new];
|
||||
for (TSThread *thread in threads) {
|
||||
if ([thread.name containsString:searchTerm]) {
|
||||
[result addObject:thread];
|
||||
} else if ([thread isKindOfClass:[TSContactThread class]]) {
|
||||
TSContactThread *contactThread = (TSContactThread *)thread;
|
||||
if (formattedNumber.length > 0 && [contactThread.contactIdentifier containsString:formattedNumber]) {
|
||||
[result addObject:thread];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Move this to contacts view helper.
|
||||
- (NSArray<Contact *> *)filteredContactsWithSearchText
|
||||
{
|
||||
// We don't want to show a 1:1 thread with Alice and Alice's contact,
|
||||
// so we de-duplicate by recipientId.
|
||||
NSArray<TSThread *> *threads = self.threadViewHelper.threads;
|
||||
NSMutableSet *contactIdsToIgnore = [NSMutableSet new];
|
||||
for (TSThread *thread in threads) {
|
||||
if ([thread isKindOfClass:[TSContactThread class]]) {
|
||||
TSContactThread *contactThread = (TSContactThread *)thread;
|
||||
[contactIdsToIgnore addObject:contactThread.contactIdentifier];
|
||||
}
|
||||
}
|
||||
|
||||
NSString *searchString = [self.searchBar text];
|
||||
|
||||
NSArray *nonRedundantContacts =
|
||||
[self.contacts filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(Contact *contact,
|
||||
NSDictionary<NSString *, id> *_Nullable bindings) {
|
||||
return ![contactIdsToIgnore containsObject:contact.textSecureIdentifiers.firstObject];
|
||||
}]];
|
||||
|
||||
// TODO: Move this to contacts view helper.
|
||||
OWSContactsSearcher *contactsSearcher = [[OWSContactsSearcher alloc] initWithContacts:nonRedundantContacts];
|
||||
NSArray<Contact *> *filteredContacts = [contactsSearcher filterWithString:searchString];
|
||||
|
||||
return filteredContacts;
|
||||
}
|
||||
|
||||
#pragma mark - Contacts and Blocking
|
||||
|
||||
- (void)blockedPhoneNumbersDidChange:(id)notification
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]];
|
||||
|
||||
[self updateContacts];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)signalRecipientsDidChange:(NSNotification *)notification
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateContacts];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateContacts
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
|
||||
self.contacts = [self filteredContacts];
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
- (BOOL)isContactBlocked:(Contact *)contact
|
||||
{
|
||||
if (contact.parsedPhoneNumbers.count < 1) {
|
||||
// Hide contacts without any valid phone numbers.
|
||||
return NO;
|
||||
}
|
||||
|
||||
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
|
||||
if ([_blockedPhoneNumberSet containsObject:phoneNumber.toE164]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isRecipientIdBlocked:(NSString *)recipientId
|
||||
{
|
||||
OWSAssert(recipientId.length > 0);
|
||||
|
||||
return [_blockedPhoneNumberSet containsObject:recipientId];
|
||||
}
|
||||
|
||||
- (BOOL)isContactHidden:(Contact *)contact
|
||||
{
|
||||
if (contact.parsedPhoneNumbers.count < 1) {
|
||||
// Hide contacts without any valid phone numbers.
|
||||
return YES;
|
||||
}
|
||||
|
||||
if ([self isCurrentUserContact:contact]) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isCurrentUserContact:(Contact *)contact
|
||||
{
|
||||
for (PhoneNumber *phoneNumber in contact.parsedPhoneNumbers) {
|
||||
if ([[phoneNumber toE164] isEqualToString:[TSAccountManager localNumber]]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSArray<Contact *> *_Nonnull)filteredContacts
|
||||
{
|
||||
NSMutableArray<Contact *> *result = [NSMutableArray new];
|
||||
for (Contact *contact in self.contactsManager.signalContacts) {
|
||||
if (![self isContactHidden:contact]) {
|
||||
[result addObject:contact];
|
||||
}
|
||||
}
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
#pragma mark - Events
|
||||
|
||||
- (void)dismissPressed:(id)sender
|
||||
{
|
||||
[self.searchBar resignFirstResponder];
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - OWSTableViewControllerDelegate
|
||||
|
||||
- (void)tableViewDidScroll
|
||||
{
|
||||
[self.searchBar resignFirstResponder];
|
||||
}
|
||||
|
||||
#pragma mark - ThreadViewHelperDelegate
|
||||
|
||||
- (void)threadListDidChange
|
||||
{
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -0,0 +1,13 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SelectThreadViewController.h"
|
||||
|
||||
@class SignalAttachment;
|
||||
|
||||
@interface SendExternalFileViewController : SelectThreadViewController
|
||||
|
||||
@property (nonatomic) SignalAttachment *attachment;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,66 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SendExternalFileViewController.h"
|
||||
#import "Environment.h"
|
||||
#import "Signal-Swift.h"
|
||||
#import "ThreadUtil.h"
|
||||
#import <SignalServiceKit/OWSMessageSender.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
@interface SendExternalFileViewController () <SelectThreadViewControllerDelegate>
|
||||
|
||||
@property (nonatomic, readonly) OWSMessageSender *messageSender;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SendExternalFileViewController
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
|
||||
self.delegate = self;
|
||||
|
||||
_messageSender = [Environment getCurrent].messageSender;
|
||||
|
||||
self.title = NSLocalizedString(@"SEND_EXTERNAL_FILE_VIEW_TITLE", @"Title for the 'send external file' view.");
|
||||
}
|
||||
|
||||
#pragma mark - SelectThreadViewControllerDelegate
|
||||
|
||||
- (void)threadWasSelected:(TSThread *)thread
|
||||
{
|
||||
OWSAssert(self.attachment);
|
||||
OWSAssert(thread);
|
||||
|
||||
// We should have a valid filename.
|
||||
OWSAssert(self.attachment.filename.length > 0);
|
||||
NSString *fileExtension = [self.attachment.filename pathExtension].lowercaseString;
|
||||
OWSAssert(fileExtension.length > 0);
|
||||
NSSet<NSString *> *textExtensions = [NSSet setWithArray:@[
|
||||
@"txt",
|
||||
@"url",
|
||||
]];
|
||||
NSString *text = nil;
|
||||
if ([textExtensions containsObject:fileExtension]) {
|
||||
text = [[NSString alloc] initWithData:self.attachment.data encoding:NSUTF8StringEncoding];
|
||||
OWSAssert(text);
|
||||
}
|
||||
|
||||
if (text) {
|
||||
[ThreadUtil sendMessageWithText:text inThread:thread messageSender:self.messageSender];
|
||||
} else {
|
||||
[ThreadUtil sendMessageWithAttachment:self.attachment inThread:thread messageSender:self.messageSender];
|
||||
}
|
||||
|
||||
[Environment messageThreadId:thread.uniqueId];
|
||||
}
|
||||
|
||||
- (BOOL)canSelectBlockedContact
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
@protocol ThreadViewHelperDelegate <NSObject>
|
||||
|
||||
- (void)threadListDidChange;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@class TSThread;
|
||||
|
||||
// A helper class
|
||||
@interface ThreadViewHelper : NSObject
|
||||
|
||||
@property (nonatomic, weak) id<ThreadViewHelperDelegate> delegate;
|
||||
|
||||
@property (nonatomic, readonly) NSMutableArray<TSThread *> *threads;
|
||||
|
||||
@end
|
||||
@ -0,0 +1,136 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ThreadViewHelper.h"
|
||||
#import <SignalServiceKit/TSDatabaseView.h>
|
||||
#import <SignalServiceKit/TSStorageManager.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
#import <YapDatabase/YapDatabaseConnection.h>
|
||||
#import <YapDatabase/YapDatabaseViewChange.h>
|
||||
#import <YapDatabase/YapDatabaseViewConnection.h>
|
||||
|
||||
@interface ThreadViewHelper ()
|
||||
|
||||
@property (nonatomic) YapDatabaseConnection *uiDatabaseConnection;
|
||||
@property (nonatomic) YapDatabaseViewMappings *threadMappings;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ThreadViewHelper
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(yapDatabaseModified:)
|
||||
name:TSUIDatabaseConnectionDidUpdateNotification
|
||||
object:nil];
|
||||
[self initializeMapping];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)initializeMapping
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
|
||||
NSString *grouping = TSInboxGroup;
|
||||
|
||||
self.threadMappings =
|
||||
[[YapDatabaseViewMappings alloc] initWithGroups:@[ grouping ] view:TSThreadDatabaseViewExtensionName];
|
||||
[self.threadMappings setIsReversed:YES forGroup:grouping];
|
||||
|
||||
__weak ThreadViewHelper *weakSelf = self;
|
||||
[self.uiDatabaseConnection asyncReadWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
[self.threadMappings updateWithTransaction:transaction];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakSelf updateThreads];
|
||||
[weakSelf.delegate threadListDidChange];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Database
|
||||
|
||||
- (YapDatabaseConnection *)uiDatabaseConnection
|
||||
{
|
||||
NSAssert([NSThread isMainThread], @"Must access uiDatabaseConnection on main thread!");
|
||||
if (!_uiDatabaseConnection) {
|
||||
YapDatabase *database = TSStorageManager.sharedManager.database;
|
||||
_uiDatabaseConnection = [database newConnection];
|
||||
[_uiDatabaseConnection beginLongLivedReadTransaction];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(yapDatabaseModified:)
|
||||
name:YapDatabaseModifiedNotification
|
||||
object:database];
|
||||
}
|
||||
return _uiDatabaseConnection;
|
||||
}
|
||||
|
||||
- (void)yapDatabaseModified:(NSNotification *)notification
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
|
||||
NSArray *notifications = [self.uiDatabaseConnection beginLongLivedReadTransaction];
|
||||
NSArray *sectionChanges = nil;
|
||||
NSArray *rowChanges = nil;
|
||||
[[self.uiDatabaseConnection ext:TSThreadDatabaseViewExtensionName] getSectionChanges:§ionChanges
|
||||
rowChanges:&rowChanges
|
||||
forNotifications:notifications
|
||||
withMappings:self.threadMappings];
|
||||
if (sectionChanges.count == 0 && rowChanges.count == 0) {
|
||||
// Ignore irrelevant modifications.
|
||||
return;
|
||||
}
|
||||
|
||||
[self updateThreads];
|
||||
|
||||
[self.delegate threadListDidChange];
|
||||
}
|
||||
|
||||
- (void)updateThreads
|
||||
{
|
||||
OWSAssert([NSThread isMainThread]);
|
||||
|
||||
NSMutableArray<TSThread *> *threads = [NSMutableArray new];
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
NSUInteger numberOfSections = [self.threadMappings numberOfSections];
|
||||
OWSAssert(numberOfSections == 1);
|
||||
for (NSUInteger section = 0; section < numberOfSections; section++) {
|
||||
NSUInteger numberOfItems = [self.threadMappings numberOfItemsInSection:section];
|
||||
for (NSUInteger item = 0; item < numberOfItems; item++) {
|
||||
TSThread *thread = [[transaction extension:TSThreadDatabaseViewExtensionName]
|
||||
objectAtIndexPath:[NSIndexPath indexPathForItem:(NSInteger)item inSection:(NSInteger)section]
|
||||
withMappings:self.threadMappings];
|
||||
[threads addObject:thread];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
_threads = threads;
|
||||
}
|
||||
|
||||
#pragma mark - Logging
|
||||
|
||||
+ (NSString *)tag
|
||||
{
|
||||
return [NSString stringWithFormat:@"[%@]", self.class];
|
||||
}
|
||||
|
||||
- (NSString *)tag
|
||||
{
|
||||
return self.class.tag;
|
||||
}
|
||||
|
||||
@end
|
||||
Loading…
Reference in New Issue