Move reminder views into scrollable content

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent 3afdd98501
commit bfe1eb5503

@ -40,6 +40,23 @@ typedef NS_ENUM(NSInteger, HomeViewMode) {
HomeViewMode_Inbox, HomeViewMode_Inbox,
}; };
// The bulk of the content in this view is driven by a YapDB view/mapping.
// However, we also want to optionally include ReminderView's at the top
// and an "Archived Conversations" button at the bottom. Rather than introduce
// index-offsets into the Mapping calculation, we introduce two pseudo groups
// to add a top and bottom section to the content, and create cells for those
// sections without consulting the YapMapping.
// This is a bit of a hack, but it consolidates the hacks into the Reminder/Archive section
// and allows us to leaves the bulk of the content logic on the happy path.
NSString *const kReminderViewPseudoGroup = @"kReminderViewPseudoGroup";
NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup";
typedef NS_ENUM(NSInteger, HomeViewControllerSection) {
HomeViewControllerSectionReminders,
HomeViewControllerSectionConversations,
HomeViewControllerSectionArchiveButton,
};
NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversationsReuseIdentifier"; NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversationsReuseIdentifier";
@interface HomeViewController () <UITableViewDelegate, @interface HomeViewController () <UITableViewDelegate,
@ -76,6 +93,8 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
// Views // Views
@property (nonatomic, readonly) UIStackView *reminderStackView;
@property (nonatomic, readonly) UITableViewCell *reminderViewCell;
@property (nonatomic, readonly) UIView *deregisteredView; @property (nonatomic, readonly) UIView *deregisteredView;
@property (nonatomic, readonly) UIView *outageView; @property (nonatomic, readonly) UIView *outageView;
@property (nonatomic, readonly) UIView *archiveReminderView; @property (nonatomic, readonly) UIView *archiveReminderView;
@ -84,6 +103,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
@property (nonatomic) TSThread *lastThread; @property (nonatomic) TSThread *lastThread;
@property (nonatomic) BOOL hasArchivedThreadsRow; @property (nonatomic) BOOL hasArchivedThreadsRow;
@property (nonatomic) BOOL hasVisibleReminders;
@end @end
@ -245,18 +265,13 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
} }
UIStackView *reminderStackView = [UIStackView new]; UIStackView *reminderStackView = [UIStackView new];
_reminderStackView = reminderStackView;
reminderStackView.axis = UILayoutConstraintAxisVertical; reminderStackView.axis = UILayoutConstraintAxisVertical;
reminderStackView.spacing = 0; reminderStackView.spacing = 0;
[self.view addSubview:reminderStackView]; _reminderViewCell = [UITableViewCell new];
[reminderStackView autoPinWidthToSuperview]; self.reminderViewCell.selectionStyle = UITableViewCellSelectionStyleNone;
[reminderStackView autoPinToTopLayoutGuideOfViewController:self withInset:0]; [self.reminderViewCell.contentView addSubview:reminderStackView];
[reminderStackView autoPinEdgesToSuperviewEdges];
// Fixes ambiguous height of an empty stack view pinned above a scroll view on iOS10.
// Without this users would sometimes see the empty stackview take up most of their screen.
[NSLayoutConstraint autoSetPriority:UILayoutPriorityDefaultLow
forConstraints:^{
[reminderStackView autoSetDimension:ALDimensionHeight toSize:0];
}];
__weak HomeViewController *weakSelf = self; __weak HomeViewController *weakSelf = self;
ReminderView *deregisteredView = ReminderView *deregisteredView =
@ -300,11 +315,8 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
[self.tableView registerClass:[HomeViewCell class] forCellReuseIdentifier:HomeViewCell.cellReuseIdentifier]; [self.tableView registerClass:[HomeViewCell class] forCellReuseIdentifier:HomeViewCell.cellReuseIdentifier];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kArchivedConversationsReuseIdentifier]; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kArchivedConversationsReuseIdentifier];
[self.view addSubview:self.tableView]; [self.view addSubview:self.tableView];
[self.tableView autoPinWidthToSuperview]; [self.tableView autoPinEdgesToSuperviewEdges];
[self.tableView autoPinEdgeToSuperviewEdge:ALEdgeBottom];
// TODO - have content scroll behind navbar will require changing this.
[self.tableView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:reminderStackView];
self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 60; self.tableView.estimatedRowHeight = 60;
@ -340,6 +352,9 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
self.missingContactsPermissionView.hidden = !self.contactsManager.isSystemContactsDenied; self.missingContactsPermissionView.hidden = !self.contactsManager.isSystemContactsDenied;
self.deregisteredView.hidden = !TSAccountManager.sharedInstance.isDeregistered; self.deregisteredView.hidden = !TSAccountManager.sharedInstance.isDeregistered;
self.outageView.hidden = !OutageDetection.sharedManager.hasOutage; self.outageView.hidden = !OutageDetection.sharedManager.hasOutage;
self.hasVisibleReminders = !self.archiveReminderView.isHidden || !self.missingContactsPermissionView.isHidden
|| !self.deregisteredView.isHidden || !self.outageView.isHidden;
} }
- (void)viewDidLoad - (void)viewDidLoad
@ -402,9 +417,8 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
self.searchResultsController = searchResultsController; self.searchResultsController = searchResultsController;
[self addChildViewController:searchResultsController]; [self addChildViewController:searchResultsController];
[self.view addSubview:searchResultsController.view]; [self.view addSubview:searchResultsController.view];
[searchResultsController.view autoPinWidthToSuperview]; [searchResultsController.view autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsZero excludingEdge:ALEdgeTop];
[searchResultsController.view autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:searchBar]; [searchResultsController.view autoPinTopToSuperviewMarginWithInset:56];
[searchResultsController.view autoPinEdge:ALEdgeBottom toEdge:ALEdgeBottom ofView:self.tableView];
searchResultsController.view.hidden = YES; searchResultsController.view.hidden = YES;
[self updateBarButtonItems]; [self updateBarButtonItems];
@ -470,23 +484,23 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
{ {
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location]; NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
if ([self isIndexPathForArchivedConversations:indexPath]) { if (!indexPath) {
return nil; return nil;
} }
if (indexPath) { if (indexPath.section != HomeViewControllerSectionConversations) {
[previewingContext setSourceRect:[self.tableView rectForRowAtIndexPath:indexPath]];
ConversationViewController *vc = [ConversationViewController new];
TSThread *thread = [self threadForIndexPath:indexPath];
self.lastThread = thread;
[vc configureForThread:thread action:ConversationViewActionNone focusMessageId:nil];
[vc peekSetup];
return vc;
} else {
return nil; return nil;
} }
[previewingContext setSourceRect:[self.tableView rectForRowAtIndexPath:indexPath]];
ConversationViewController *vc = [ConversationViewController new];
TSThread *thread = [self threadForIndexPath:indexPath];
self.lastThread = thread;
[vc configureForThread:thread action:ConversationViewActionNone focusMessageId:nil];
[vc peekSetup];
return vc;
} }
- (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext - (void)previewingContext:(id<UIViewControllerPreviewing>)previewingContext
@ -542,7 +556,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
BOOL isShowingSearchResults = !self.searchResultsController.view.hidden; BOOL isShowingSearchResults = !self.searchResultsController.view.hidden;
if (isShowingSearchResults) { if (isShowingSearchResults) {
OWSAssert(self.searchBar.text.ows_stripped.length > 0); OWSAssert(self.searchBar.text.ows_stripped.length > 0);
self.tableView.contentOffset = CGPointZero; [self scrollSearchBarToTopAnimated:NO];
} else if (self.lastThread) { } else if (self.lastThread) {
OWSAssert(self.searchBar.text.ows_stripped.length == 0); OWSAssert(self.searchBar.text.ows_stripped.length == 0);
@ -734,31 +748,30 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
return (NSInteger)[self.threadMappings numberOfSections]; return (NSInteger)[self.threadMappings numberOfSections];
} }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)aSection
{ {
NSInteger result = (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section]; HomeViewControllerSection section = (HomeViewControllerSection)aSection;
if (self.hasArchivedThreadsRow) { switch (section) {
// Add the "archived conversations" row. case HomeViewControllerSectionReminders: {
result++; return self.hasVisibleReminders ? 1 : 0;
}
case HomeViewControllerSectionConversations: {
NSInteger result = (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section];
return result;
}
case HomeViewControllerSectionArchiveButton: {
return self.hasArchivedThreadsRow ? 1 : 0;
}
} }
return result;
}
- (BOOL)isIndexPathForArchivedConversations:(NSIndexPath *)indexPath OWSFail(@"%@ failure: unexpected section: %lu", self.logTag, (unsigned long)section);
{ return 0;
if (self.homeViewMode != HomeViewMode_Inbox) {
return NO;
}
if (indexPath.section != 0) {
return NO;
}
NSInteger cellCount = (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)0];
return indexPath.row == cellCount;
} }
- (ThreadViewModel *)threadViewModelForIndexPath:(NSIndexPath *)indexPath - (ThreadViewModel *)threadViewModelForIndexPath:(NSIndexPath *)indexPath
{ {
TSThread *threadRecord = [self threadForIndexPath:indexPath]; TSThread *threadRecord = [self threadForIndexPath:indexPath];
OWSAssert(threadRecord);
ThreadViewModel *_Nullable cachedThreadViewModel = [self.threadViewModelCache objectForKey:threadRecord.uniqueId]; ThreadViewModel *_Nullable cachedThreadViewModel = [self.threadViewModelCache objectForKey:threadRecord.uniqueId];
if (cachedThreadViewModel) { if (cachedThreadViewModel) {
@ -775,11 +788,21 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ {
if ([self isIndexPathForArchivedConversations:indexPath]) { HomeViewControllerSection section = (HomeViewControllerSection)indexPath.section;
return [self cellForArchivedConversationsRow:tableView]; switch (section) {
} else { case HomeViewControllerSectionReminders: {
return [self tableView:tableView cellForConversationAtIndexPath:indexPath]; return self.reminderViewCell;
}
case HomeViewControllerSectionConversations: {
return [self tableView:tableView cellForConversationAtIndexPath:indexPath];
}
case HomeViewControllerSectionArchiveButton: {
return [self cellForArchivedConversationsRow:tableView];
}
} }
OWSFail(@"%@ failure: unexpected section: %lu", self.logTag, (unsigned long)section);
return [UITableViewCell new];
} }
- (UITableViewCell *)tableView:(UITableView *)tableView cellForConversationAtIndexPath:(NSIndexPath *)indexPath - (UITableViewCell *)tableView:(UITableView *)tableView cellForConversationAtIndexPath:(NSIndexPath *)indexPath
@ -875,56 +898,70 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
- (nullable NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath - (nullable NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{ {
if ([self isIndexPathForArchivedConversations:indexPath]) { HomeViewControllerSection section = (HomeViewControllerSection)indexPath.section;
return @[]; switch (section) {
} case HomeViewControllerSectionReminders: {
return @[];
UITableViewRowAction *deleteAction = }
[UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault case HomeViewControllerSectionConversations: {
title:NSLocalizedString(@"TXT_DELETE_TITLE", nil) UITableViewRowAction *deleteAction =
handler:^(UITableViewRowAction *action, NSIndexPath *swipedIndexPath) { [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault
[self tableViewCellTappedDelete:swipedIndexPath]; title:NSLocalizedString(@"TXT_DELETE_TITLE", nil)
}]; handler:^(UITableViewRowAction *action, NSIndexPath *swipedIndexPath) {
[self tableViewCellTappedDelete:swipedIndexPath];
UITableViewRowAction *archiveAction; }];
if (self.homeViewMode == HomeViewMode_Inbox) {
archiveAction = [UITableViewRowAction UITableViewRowAction *archiveAction;
rowActionWithStyle:UITableViewRowActionStyleNormal if (self.homeViewMode == HomeViewMode_Inbox) {
title:NSLocalizedString(@"ARCHIVE_ACTION", archiveAction = [UITableViewRowAction
@"Pressing this button moves a thread from the inbox to the archive") rowActionWithStyle:UITableViewRowActionStyleNormal
handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull tappedIndexPath) { title:NSLocalizedString(@"ARCHIVE_ACTION",
[self archiveIndexPath:tappedIndexPath]; @"Pressing this button moves a thread from the inbox to the archive")
[Environment.preferences setHasArchivedAMessage:YES]; handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull tappedIndexPath) {
}]; [self archiveIndexPath:tappedIndexPath];
[Environment.preferences setHasArchivedAMessage:YES];
}];
} else {
archiveAction = [UITableViewRowAction
rowActionWithStyle:UITableViewRowActionStyleNormal
title:NSLocalizedString(@"UNARCHIVE_ACTION",
@"Pressing this button moves an archived thread from the archive back to "
@"the inbox")
handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull tappedIndexPath) {
[self archiveIndexPath:tappedIndexPath];
}];
}
} else { return @[ deleteAction, archiveAction ];
archiveAction = [UITableViewRowAction }
rowActionWithStyle:UITableViewRowActionStyleNormal case HomeViewControllerSectionArchiveButton: {
title:NSLocalizedString(@"UNARCHIVE_ACTION", return @[];
@"Pressing this button moves an archived thread from the archive back to the inbox") }
handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull tappedIndexPath) {
[self archiveIndexPath:tappedIndexPath];
}];
} }
return @[ deleteAction, archiveAction ];
} }
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{ {
if ([self isIndexPathForArchivedConversations:indexPath]) { HomeViewControllerSection section = (HomeViewControllerSection)indexPath.section;
return NO; switch (section) {
case HomeViewControllerSectionReminders: {
return NO;
}
case HomeViewControllerSectionConversations: {
return YES;
}
case HomeViewControllerSectionArchiveButton: {
return NO;
}
} }
return YES;
} }
#pragma mark - UISearchBarDelegate #pragma mark - UISearchBarDelegate
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{ {
[self.tableView setContentOffset:CGPointZero animated:NO]; [self scrollSearchBarToTopAnimated:NO];
[self updateSearchResultsVisibility]; [self updateSearchResultsVisibility];
@ -977,13 +1014,19 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
self.searchResultsController.view.hidden = !isSearching; self.searchResultsController.view.hidden = !isSearching;
if (isSearching) { if (isSearching) {
[self.tableView setContentOffset:CGPointZero animated:NO]; [self scrollSearchBarToTopAnimated:NO];
self.tableView.scrollEnabled = NO; self.tableView.scrollEnabled = NO;
} else { } else {
self.tableView.scrollEnabled = YES; self.tableView.scrollEnabled = YES;
} }
} }
- (void)scrollSearchBarToTopAnimated:(BOOL)isAnimated
{
CGFloat topInset = self.topLayoutGuide.length;
[self.tableView setContentOffset:CGPointMake(0, -topInset) animated:isAnimated];
}
#pragma mark - UIScrollViewDelegate #pragma mark - UIScrollViewDelegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
@ -1004,6 +1047,11 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
- (void)tableViewCellTappedDelete:(NSIndexPath *)indexPath - (void)tableViewCellTappedDelete:(NSIndexPath *)indexPath
{ {
if (indexPath.section != HomeViewControllerSectionConversations) {
OWSFail(@"%@ failure: unexpected section: %lu", self.logTag, (unsigned long)indexPath.section);
return;
}
TSThread *thread = [self threadForIndexPath:indexPath]; TSThread *thread = [self threadForIndexPath:indexPath];
if ([thread isKindOfClass:[TSGroupThread class]]) { if ([thread isKindOfClass:[TSGroupThread class]]) {
@ -1055,6 +1103,11 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
- (void)archiveIndexPath:(NSIndexPath *)indexPath - (void)archiveIndexPath:(NSIndexPath *)indexPath
{ {
if (indexPath.section != HomeViewControllerSectionConversations) {
OWSFail(@"%@ failure: unexpected section: %lu", self.logTag, (unsigned long)indexPath.section);
return;
}
TSThread *thread = [self threadForIndexPath:indexPath]; TSThread *thread = [self threadForIndexPath:indexPath];
[self.editingDbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [self.editingDbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@ -1075,15 +1128,22 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
DDLogInfo(@"%@ %s %ld %ld", self.logTag, __PRETTY_FUNCTION__, (long)indexPath.row, (long)indexPath.section); DDLogInfo(@"%@ %s %ld %ld", self.logTag, __PRETTY_FUNCTION__, (long)indexPath.row, (long)indexPath.section);
[self.searchBar resignFirstResponder]; [self.searchBar resignFirstResponder];
HomeViewControllerSection section = (HomeViewControllerSection)indexPath.section;
if ([self isIndexPathForArchivedConversations:indexPath]) { switch (section) {
[self showArchivedConversations]; case HomeViewControllerSectionReminders: {
return; break;
}
case HomeViewControllerSectionConversations: {
TSThread *thread = [self threadForIndexPath:indexPath];
[self presentThread:thread action:ConversationViewActionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
break;
}
case HomeViewControllerSectionArchiveButton: {
[self showArchivedConversations];
break;
}
} }
TSThread *thread = [self threadForIndexPath:indexPath];
[self presentThread:thread action:ConversationViewActionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
} }
- (void)presentThread:(TSThread *)thread action:(ConversationViewAction)action - (void)presentThread:(TSThread *)thread action:(ConversationViewAction)action
@ -1218,8 +1278,9 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
self.threadMappings = [[YapDatabaseViewMappings alloc] initWithGroups:@[ self.currentGrouping ] self.threadMappings = [[YapDatabaseViewMappings alloc]
view:TSThreadDatabaseViewExtensionName]; initWithGroups:@[ kReminderViewPseudoGroup, self.currentGrouping, kArchiveButtonPseudoGroup ]
view:TSThreadDatabaseViewExtensionName];
[self.threadMappings setIsReversed:YES forGroup:self.currentGrouping]; [self.threadMappings setIsReversed:YES forGroup:self.currentGrouping];
[self resetMappings]; [self resetMappings];

Loading…
Cancel
Save