Merge branch 'charlesmchen/archivedConversations'

This commit is contained in:
Matthew Chen 2018-04-24 11:42:50 -04:00
commit 2c1e633914
17 changed files with 284 additions and 245 deletions

View File

@ -144,6 +144,23 @@ DEPENDENCIES:
- YapDatabase/SQLCipher (from `https://github.com/signalapp/YapDatabase.git`, branch `release/unencryptedHeaders`)
- YYImage
SPEC REPOS:
https://github.com/CocoaPods/Specs.git:
- AFNetworking
- ATAppUpdater
- CocoaLumberjack
- JSQSystemSoundPlayer
- libPhoneNumber-iOS
- Mantle
- ProtocolBuffers
- PureLayout
- Reachability
- SAMKeychain
- SSZipArchive
- TwistedOakCollapsingFutures
- UnionFind
- YYImage
EXTERNAL SOURCES:
AxolotlKit:
:git: https://github.com/signalapp/SignalProtocolKit.git
@ -159,7 +176,7 @@ EXTERNAL SOURCES:
:branch: mkirk/share-compatible
:git: https://github.com/signalapp/JSQMessagesViewController.git
SignalServiceKit:
:path: .
:path: "."
SocketRocket:
:git: https://github.com/facebook/SocketRocket.git
SQLCipher:
@ -222,4 +239,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 6a1bafb7c5bedfa4e577580ff12e487cc7111f38
COCOAPODS: 1.4.0
COCOAPODS: 1.5.0

View File

@ -2412,7 +2412,6 @@
453518641FC635DD00210559 /* Sources */,
453518651FC635DD00210559 /* Frameworks */,
453518661FC635DD00210559 /* Resources */,
7B85A55670DC3D49AFBF7359 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -2433,7 +2432,6 @@
4535188E1FC63DBF00210559 /* Frameworks */,
4535188F1FC63DBF00210559 /* Headers */,
453518901FC63DBF00210559 /* Resources */,
6C612B7B27FC78638EB7B113 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@ -2454,7 +2452,6 @@
D221A086169C9E5E00537ABF /* Frameworks */,
D221A087169C9E5E00537ABF /* Resources */,
59C9DBA462715B5C999FFB02 /* [CP] Embed Pods Frameworks */,
3465F381B1856CC06933B3A8 /* [CP] Copy Pods Resources */,
451DE9EE1DC1546A00810E42 /* [Carthage] Copy Frameworks */,
453518771FC635DD00210559 /* Embed App Extensions */,
4535189F1FC63DBF00210559 /* Embed Frameworks */,
@ -2479,7 +2476,6 @@
D221A0A6169C9E5F00537ABF /* Frameworks */,
D221A0A7169C9E5F00537ABF /* Resources */,
B4E9B04E862FB64FC9A8F79B /* [CP] Embed Pods Frameworks */,
F76686434770E2BBEBD9665A /* [CP] Copy Pods Resources */,
451DE9FB1DC18D4500810E42 /* [Carthage] Copy Frameworks */,
);
buildRules = (
@ -2780,39 +2776,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3465F381B1856CC06933B3A8 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-Signal/Pods-Signal-resources.sh",
"${PODS_ROOT}/JSQMessagesViewController/JSQMessagesViewController/Assets/JSQMessagesAssets.bundle",
"${PODS_ROOT}/JSQMessagesViewController/JSQMessagesViewController/Controllers/JSQMessagesViewController.xib",
"${PODS_ROOT}/JSQMessagesViewController/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellIncoming.xib",
"${PODS_ROOT}/JSQMessagesViewController/JSQMessagesViewController/Views/JSQMessagesCollectionViewCellOutgoing.xib",
"${PODS_ROOT}/JSQMessagesViewController/JSQMessagesViewController/Views/JSQMessagesLoadEarlierHeaderView.xib",
"${PODS_ROOT}/JSQMessagesViewController/JSQMessagesViewController/Views/JSQMessagesToolbarContentView.xib",
"${PODS_ROOT}/JSQMessagesViewController/JSQMessagesViewController/Views/JSQMessagesTypingIndicatorFooterView.xib",
"${PODS_ROOT}/SAMKeychain/Support/SAMKeychain.bundle",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/textsecure.cer",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GIAG2.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GSR2.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GSR4.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GTSR1.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GTSR2.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GTSR3.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GTSR4.crt",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Signal/Pods-Signal-resources.sh\"\n";
showEnvVarsInLog = 0;
};
451DE9EE1DC1546A00810E42 /* [Carthage] Copy Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -2941,47 +2904,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
6C612B7B27FC78638EB7B113 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalMessaging/Pods-SignalMessaging-resources.sh\"\n";
showEnvVarsInLog = 0;
};
7B85A55670DC3D49AFBF7359 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${SRCROOT}/Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension-resources.sh",
"${PODS_ROOT}/SAMKeychain/Support/SAMKeychain.bundle",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/textsecure.cer",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GIAG2.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GSR2.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GSR4.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GTSR1.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GTSR2.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GTSR3.crt",
"${PODS_ROOT}/../SignalServiceKit/src/Security/PinningCertificate/GTSR4.crt",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension-resources.sh\"\n";
showEnvVarsInLog = 0;
};
B4E9B04E862FB64FC9A8F79B /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -3058,21 +2980,6 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
F76686434770E2BBEBD9665A /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SignalTests/Pods-SignalTests-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -3512,11 +3419,7 @@
"DEBUG=1",
"$(inherited)",
);
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = (
"DEBUG=1",
"$(inherited)",
"SSK_BUILDING_FOR_TESTS=1",
);
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = "DEBUG=1 $(inherited) SSK_BUILDING_FOR_TESTS=1";
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "DisclosureIndicatorRTL@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "DisclosureIndicatorRTL@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "DisclosureIndicatorRTL@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -2,7 +2,6 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "HomeViewController.h"
#import <UIKit/UIKit.h>
extern NSString *const AppDelegateStoryboardMain;

View File

@ -6,6 +6,7 @@
#import "AppStoreRating.h"
#import "AppUpdateNag.h"
#import "CodeVerificationViewController.h"
#import "HomeViewController.h"
#import "DebugLogger.h"
#import "MainAppContext.h"
#import "NotificationsManager.h"
@ -707,14 +708,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
DDLogWarn(@"%@ applicationWillResignActive.", self.logTag);
__block OWSBackgroundTask *backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
[AppReadiness runNowOrWhenAppIsReady:^{
if ([TSAccountManager isRegistered]) {
[SignalApp.sharedApp.homeViewController updateInboxCountLabel];
}
backgroundTask = nil;
}];
[DDLog flushLog];
}

View File

@ -13,8 +13,6 @@
keyboardOnViewAppearing:(BOOL)keyboardOnViewAppearing
callOnViewAppearing:(BOOL)callOnViewAppearing;
- (void)updateInboxCountLabel;
- (void)showNewConversationView;
- (void)presentTopLevelModalViewController:(UIViewController *)viewController

View File

@ -32,7 +32,12 @@
#import <YapDatabase/YapDatabaseViewChange.h>
#import <YapDatabase/YapDatabaseViewConnection.h>
typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
typedef NS_ENUM(NSInteger, HomeViewMode) {
HomeViewMode_Archive,
HomeViewMode_Inbox,
};
NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversationsReuseIdentifier";
@interface HomeViewController () <UITableViewDelegate, UITableViewDataSource, UIViewControllerPreviewingDelegate>
@ -42,9 +47,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
@property (nonatomic) YapDatabaseConnection *editingDbConnection;
@property (nonatomic) YapDatabaseConnection *uiDatabaseConnection;
@property (nonatomic) YapDatabaseViewMappings *threadMappings;
@property (nonatomic) CellState viewingThreadsIn;
@property (nonatomic) long inboxCount;
@property (nonatomic) UISegmentedControl *segmentedControl;
@property (nonatomic) HomeViewMode homeViewMode;
@property (nonatomic) id previewingContext;
@property (nonatomic) NSSet<NSString *> *blockedPhoneNumberSet;
@property (nonatomic, readonly) NSCache<NSString *, ThreadViewModel *> *threadViewModelCache;
@ -82,6 +85,8 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
return self;
}
_homeViewMode = HomeViewMode_Inbox;
[self commonInit];
return self;
@ -177,30 +182,21 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
// TODO: Remove this.
[SignalApp.sharedApp setHomeViewController:self];
self.navigationItem.rightBarButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose
target:self
action:@selector(showNewConversationView)];
ReminderView *archiveReminderView = [ReminderView new];
archiveReminderView.text = NSLocalizedString(
@"INBOX_VIEW_ARCHIVE_MODE_REMINDER", @"Label reminding the user that they are in archive mode.");
__weak HomeViewController *weakSelf = self;
archiveReminderView.tapAction = ^{
[weakSelf showInboxGrouping];
};
ReminderView *archiveReminderView =
[ReminderView explanationWithText:NSLocalizedString(@"INBOX_VIEW_ARCHIVE_MODE_REMINDER",
@"Label reminding the user that they are in archive mode.")];
[self.view addSubview:archiveReminderView];
[archiveReminderView autoPinWidthToSuperview];
[archiveReminderView autoPinToTopLayoutGuideOfViewController:self withInset:0];
self.hideArchiveReminderViewConstraint = [archiveReminderView autoSetDimension:ALDimensionHeight toSize:0];
self.hideArchiveReminderViewConstraint.priority = UILayoutPriorityRequired;
ReminderView *missingContactsPermissionView = [ReminderView new];
missingContactsPermissionView.text = NSLocalizedString(@"INBOX_VIEW_MISSING_CONTACTS_PERMISSION",
@"Multi-line label explaining how to show names instead of phone numbers in your inbox");
missingContactsPermissionView.tapAction = ^{
[[UIApplication sharedApplication] openSystemSettings];
};
ReminderView *missingContactsPermissionView = [ReminderView
nagWithText:NSLocalizedString(@"INBOX_VIEW_MISSING_CONTACTS_PERMISSION",
@"Multi-line label explaining how to show names instead of phone numbers in your inbox")
tapAction:^{
[[UIApplication sharedApplication] openSystemSettings];
}];
[self.view addSubview:missingContactsPermissionView];
[missingContactsPermissionView autoPinWidthToSuperview];
[missingContactsPermissionView autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:archiveReminderView];
@ -213,6 +209,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
self.tableView.dataSource = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.tableView registerClass:[HomeViewCell class] forCellReuseIdentifier:HomeViewCell.cellReuseIdentifier];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kArchivedConversationsReuseIdentifier];
[self.view addSubview:self.tableView];
[self.tableView autoPinWidthToSuperview];
[self.tableView autoPinEdgeToSuperviewEdge:ALEdgeBottom];
@ -237,7 +234,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
- (void)updateReminderViews
{
BOOL shouldHideArchiveReminderView = self.viewingThreadsIn != kArchiveState;
BOOL shouldHideArchiveReminderView = self.homeViewMode != HomeViewMode_Archive;
BOOL shouldHideMissingContactsPermissionView = !self.shouldShowMissingContactsPermissionView;
if (self.hideArchiveReminderViewConstraint.active == shouldHideArchiveReminderView
&& self.hideMissingContactsPermissionViewConstraint.active == shouldHideMissingContactsPermissionView) {
@ -259,25 +256,28 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
// Create the database connection.
[self uiDatabaseConnection];
[self showInboxGrouping];
[self updateMappings];
[self checkIfEmptyView];
[self updateReminderViews];
// because this uses the table data source, `tableViewSetup` must happen
// after mappings have been set up in `showInboxGrouping`
[self tableViewSetUp];
self.segmentedControl = [[UISegmentedControl alloc] initWithItems:@[
NSLocalizedString(@"WHISPER_NAV_BAR_TITLE", nil),
NSLocalizedString(@"ARCHIVE_NAV_BAR_TITLE", nil)
]];
[self.segmentedControl addTarget:self
action:@selector(swappedSegmentedControl)
forControlEvents:UIControlEventValueChanged];
UINavigationItem *navigationItem = self.navigationItem;
navigationItem.titleView = self.segmentedControl;
[self.segmentedControl setSelectedSegmentIndex:0];
navigationItem.leftBarButtonItem.accessibilityLabel
= NSLocalizedString(@"SETTINGS_BUTTON_ACCESSIBILITY", @"Accessibility hint for the settings button");
switch (self.homeViewMode) {
case HomeViewMode_Inbox:
// TODO: Should our app name be translated? Probably not.
self.title = NSLocalizedString(@"HOME_VIEW_TITLE_INBOX", @"Title for the home view's default mode.");
break;
case HomeViewMode_Archive:
self.title = NSLocalizedString(@"HOME_VIEW_TITLE_ARCHIVE", @"Title for the home view's 'archive' mode.");
break;
}
self.navigationItem.backBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"BACK_BUTTON", @"button text for back button")
style:UIBarButtonItemStylePlain
target:nil
action:nil];
if ([self.traitCollection respondsToSelector:@selector(forceTouchCapability)]
&& (self.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable)) {
@ -296,6 +296,9 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
- (void)updateBarButtonItems
{
if (self.homeViewMode != HomeViewMode_Inbox) {
return;
}
const CGFloat kBarButtonSize = 44;
// We use UIButtons with [UIBarButtonItem initWithCustomView:...] instead of
// UIBarButtonItem in order to ensure that these buttons are spaced tightly.
@ -327,7 +330,15 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
0,
round(image.size.width + imageEdgeInsets.left + imageEdgeInsets.right),
round(image.size.height + imageEdgeInsets.top + imageEdgeInsets.bottom));
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithCustomView:button];
settingsButton.accessibilityLabel
= NSLocalizedString(@"SETTINGS_BUTTON_ACCESSIBILITY", @"Accessibility hint for the settings button");
self.navigationItem.leftBarButtonItem = settingsButton;
self.navigationItem.rightBarButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose
target:self
action:@selector(showNewConversationView)];
}
- (void)settingsButtonPressed:(id)sender
@ -367,6 +378,10 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
- (void)showNewConversationView
{
OWSAssertIsOnMainThread();
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
NewContactThreadViewController *viewController = [NewContactThreadViewController new];
[self.contactsManager requestSystemContactsOnceWithCompletion:^(NSError *_Nullable error) {
@ -384,15 +399,6 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
}];
}
- (void)swappedSegmentedControl
{
if (self.segmentedControl.selectedSegmentIndex == 0) {
[self showInboxGrouping];
} else {
[self showArchiveGrouping];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
@ -408,8 +414,6 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
}];
}
[self updateInboxCountLabel];
self.isViewVisible = YES;
// When returning to home view, try to ensure that the "last" thread is still
@ -499,7 +503,6 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
[self reloadTableViewData];
[self checkIfEmptyView];
[self updateInboxCountLabel];
// If the user hasn't already granted contact access
// we don't want to request until they receive a message.
@ -601,7 +604,24 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section];
NSInteger result = (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section];
if (self.homeViewMode == HomeViewMode_Inbox) {
// Add the "archived conversations" row.
result++;
}
return result;
}
- (BOOL)isIndexPathForArchivedConversations:(NSIndexPath *)indexPath
{
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
@ -622,6 +642,15 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self isIndexPathForArchivedConversations:indexPath]) {
return [self cellForArchivedConversationsRow:tableView];
} else {
return [self tableView:tableView cellForConversationAtIndexPath:indexPath];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForConversationAtIndexPath:(NSIndexPath *)indexPath
{
HomeViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:HomeViewCell.cellReuseIdentifier];
OWSAssert(cell);
@ -638,6 +667,47 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
return cell;
}
- (UITableViewCell *)cellForArchivedConversationsRow:(UITableView *)tableView
{
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:kArchivedConversationsReuseIdentifier];
OWSAssert(cell);
for (UIView *subview in cell.contentView.subviews) {
[subview removeFromSuperview];
}
cell.backgroundColor = [UIColor whiteColor];
UIImage *disclosureImage = [UIImage imageNamed:(cell.isRTL ? @"NavBarBack" : @"NavBarBackRTL")];
OWSAssert(disclosureImage);
UIImageView *disclosureImageView = [UIImageView new];
disclosureImageView.image = [disclosureImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
disclosureImageView.tintColor = [UIColor colorWithRGBHex:0xd1d1d6];
[disclosureImageView setContentHuggingHigh];
[disclosureImageView setCompressionResistanceHigh];
UILabel *label = [UILabel new];
label.text = NSLocalizedString(@"HOME_VIEW_ARCHIVED_CONVERSATIONS", @"Label for 'archived conversations' button.");
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont ows_dynamicTypeBodyFont];
label.textColor = [UIColor blackColor];
UIStackView *stackView = [UIStackView new];
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.spacing = 5;
[stackView addArrangedSubview:label];
[stackView addArrangedSubview:disclosureImageView];
[cell.contentView addSubview:stackView];
[stackView autoCenterInSuperview];
// Constrain to cell margins.
[stackView autoPinEdgeToSuperviewMargin:ALEdgeTop relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewMargin:ALEdgeBottom relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewMargin:ALEdgeLeading relation:NSLayoutRelationGreaterThanOrEqual];
[stackView autoPinEdgeToSuperviewMargin:ALEdgeTrailing relation:NSLayoutRelationGreaterThanOrEqual];
return cell;
}
- (TSThread *)threadForIndexPath:(NSIndexPath *)indexPath
{
__block TSThread *thread = nil;
@ -675,6 +745,10 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self isIndexPathForArchivedConversations:indexPath]) {
return @[];
}
UITableViewRowAction *deleteAction =
[UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault
title:NSLocalizedString(@"TXT_DELETE_TITLE", nil)
@ -683,7 +757,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
}];
UITableViewRowAction *archiveAction;
if (self.viewingThreadsIn == kInboxState) {
if (self.homeViewMode == HomeViewMode_Inbox) {
archiveAction = [UITableViewRowAction
rowActionWithStyle:UITableViewRowActionStyleNormal
title:NSLocalizedString(@"ARCHIVE_ACTION",
@ -761,7 +835,6 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
[thread removeWithTransaction:transaction];
}];
_inboxCount -= (self.viewingThreadsIn == kArchiveState) ? 1 : 0;
[self checkIfEmptyView];
}
@ -769,31 +842,26 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
{
TSThread *thread = [self threadForIndexPath:indexPath];
BOOL viewingThreadsIn = self.viewingThreadsIn;
[self.editingDbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
viewingThreadsIn == kInboxState ? [thread archiveThreadWithTransaction:transaction]
: [thread unarchiveThreadWithTransaction:transaction];
switch (self.homeViewMode) {
case HomeViewMode_Inbox:
[thread archiveThreadWithTransaction:transaction];
break;
case HomeViewMode_Archive:
[thread unarchiveThreadWithTransaction:transaction];
break;
}
}];
[self checkIfEmptyView];
}
- (void)updateInboxCountLabel
{
NSUInteger numberOfItems = [OWSMessageUtils.sharedManager unreadMessagesCount];
NSString *unreadString = NSLocalizedString(@"WHISPER_NAV_BAR_TITLE", nil);
if (numberOfItems > 0) {
unreadString = [unreadString stringByAppendingFormat:@" (%@)", [OWSFormat formatInt:(int)numberOfItems]];
}
[_segmentedControl setTitle:unreadString forSegmentAtIndex:0];
[_segmentedControl sizeToFit];
[_segmentedControl.superview setNeedsLayout];
[_segmentedControl reloadInputViews];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self isIndexPathForArchivedConversations:indexPath]) {
[self showArchivedConversations];
return;
}
TSThread *thread = [self threadForIndexPath:indexPath];
[self presentThread:thread keyboardOnViewAppearing:NO callOnViewAppearing:NO];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
@ -898,30 +966,29 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
- (void)showInboxGrouping
{
self.viewingThreadsIn = kInboxState;
OWSAssert(self.homeViewMode == HomeViewMode_Archive);
[self.navigationController popToRootViewControllerAnimated:YES];
}
- (void)showArchiveGrouping
- (void)showArchivedConversations
{
self.viewingThreadsIn = kArchiveState;
}
OWSAssert(self.homeViewMode == HomeViewMode_Inbox);
- (void)setViewingThreadsIn:(CellState)viewingThreadsIn
{
BOOL didChange = _viewingThreadsIn != viewingThreadsIn;
_viewingThreadsIn = viewingThreadsIn;
self.segmentedControl.selectedSegmentIndex = (viewingThreadsIn == kInboxState ? 0 : 1);
if (didChange || !self.threadMappings) {
[self updateMappings];
} else {
[self checkIfEmptyView];
[self updateReminderViews];
}
// Push a separate instance of this view using "archive" mode.
HomeViewController *homeView = [HomeViewController new];
homeView.homeViewMode = HomeViewMode_Archive;
[self.navigationController pushViewController:homeView animated:YES];
}
- (NSString *)currentGrouping
{
return self.viewingThreadsIn == kInboxState ? TSInboxGroup : TSArchiveGroup;
switch (self.homeViewMode) {
case HomeViewMode_Inbox:
return TSInboxGroup;
case HomeViewMode_Archive:
return TSArchiveGroup;
}
}
- (void)updateMappings
@ -986,8 +1053,10 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
if (![[self.uiDatabaseConnection ext:TSThreadDatabaseViewExtensionName] hasChangesForGroup:self.currentGrouping
inNotifications:notifications]) {
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[self.self.threadMappings updateWithTransaction:transaction];
[self.threadMappings updateWithTransaction:transaction];
}];
[self checkIfEmptyView];
return;
}
@ -1011,7 +1080,6 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
// We want this regardless of if we're currently viewing the archive.
// So we run it before the early return
[self updateInboxCountLabel];
[self checkIfEmptyView];
if ([sectionChanges count] == 0 && [rowChanges count] == 0) {
@ -1047,13 +1115,11 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
case YapDatabaseViewChangeDelete: {
[self.tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
_inboxCount += (self.viewingThreadsIn == kArchiveState) ? 1 : 0;
break;
}
case YapDatabaseViewChangeInsert: {
[self.tableView insertRowsAtIndexPaths:@[ rowChange.newIndexPath ]
withRowAnimation:UITableViewRowAnimationAutomatic];
_inboxCount -= (self.viewingThreadsIn == kArchiveState) ? 1 : 0;
break;
}
case YapDatabaseViewChangeMove: {
@ -1076,21 +1142,31 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
- (void)checkIfEmptyView
{
[_tableView setHidden:NO];
[_emptyBoxLabel setHidden:NO];
if (self.viewingThreadsIn == kInboxState && [self.threadMappings numberOfItemsInGroup:TSInboxGroup] == 0) {
[self setEmptyBoxText];
// We need to consult the db view, not the mapping since the mapping only knows about
// the current group.
__block NSUInteger inboxCount;
__block NSUInteger archiveCount;
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
YapDatabaseViewTransaction *viewTransaction = [transaction ext:TSThreadDatabaseViewExtensionName];
inboxCount = [viewTransaction numberOfItemsInGroup:TSInboxGroup];
archiveCount = [viewTransaction numberOfItemsInGroup:TSArchiveGroup];
}];
if (self.homeViewMode == HomeViewMode_Inbox && inboxCount == 0 && archiveCount == 0) {
[self updateEmptyBoxText];
[_tableView setHidden:YES];
} else if (self.viewingThreadsIn == kArchiveState &&
[self.threadMappings numberOfItemsInGroup:TSArchiveGroup] == 0) {
[self setEmptyBoxText];
[_emptyBoxLabel setHidden:NO];
} else if (self.homeViewMode == HomeViewMode_Archive && archiveCount == 0) {
[self updateEmptyBoxText];
[_tableView setHidden:YES];
[_emptyBoxLabel setHidden:NO];
} else {
[_emptyBoxLabel setHidden:YES];
[_tableView setHidden:NO];
}
}
- (void)setEmptyBoxText
- (void)updateEmptyBoxText
{
_emptyBoxLabel.textColor = [UIColor grayColor];
_emptyBoxLabel.font = [UIFont ows_regularFontWithSize:18.f];
@ -1100,7 +1176,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
NSString *firstLine = @"";
NSString *secondLine = @"";
if (self.viewingThreadsIn == kInboxState) {
if (self.homeViewMode == HomeViewMode_Inbox) {
if ([Environment.preferences getHasSentAMessage]) {
firstLine = NSLocalizedString(@"EMPTY_INBOX_FIRST_TITLE", @"");
secondLine = NSLocalizedString(@"EMPTY_INBOX_FIRST_TEXT", @"");

View File

@ -82,12 +82,12 @@ NS_ASSUME_NONNULL_BEGIN
_nonContactAccountSet = [NSMutableSet set];
_collation = [UILocalizedIndexedCollation currentCollation];
ReminderView *contactsPermissionReminderView = [[ReminderView alloc]
initWithText:NSLocalizedString(@"COMPOSE_SCREEN_MISSING_CONTACTS_PERMISSION",
@"Multi-line label explaining why compose-screen contact picker is empty.")
tapAction:^{
[[UIApplication sharedApplication] openSystemSettings];
}];
ReminderView *contactsPermissionReminderView =
[ReminderView nagWithText:NSLocalizedString(@"COMPOSE_SCREEN_MISSING_CONTACTS_PERMISSION",
@"Multi-line label explaining why compose-screen contact picker is empty.")
tapAction:^{
[[UIApplication sharedApplication] openSystemSettings];
}];
[self.view addSubview:contactsPermissionReminderView];
[contactsPermissionReminderView autoPinWidthToSuperview];
[contactsPermissionReminderView autoPinEdgeToSuperviewMargin:ALEdgeTop];

View File

@ -3,7 +3,6 @@
//
#import "ShowGroupMembersViewController.h"
#import "HomeViewController.h"
#import "Signal-Swift.h"
#import "SignalApp.h"
#import "ViewControllerUtils.h"

View File

@ -211,9 +211,12 @@
keyboardOnViewAppearing:(BOOL)keyboardOnViewAppearing
callOnViewAppearing:(BOOL)callOnViewAppearing
{
OWSAssertIsOnMainThread();
// At most one.
OWSAssert(!keyboardOnViewAppearing || !callOnViewAppearing);
DDLogInfo(@"%@ %s", self.logTag, __PRETTY_FUNCTION__);
if (!thread) {
OWSFail(@"%@ Can't present nil thread.", self.logTag);
return;

View File

@ -288,7 +288,6 @@ NSString *const Signal_Message_MarkAsRead_Identifier = @"Signal_Message_MarkAsRe
[thread markAllAsReadWithTransaction:transaction];
}
completionBlock:^{
[SignalApp.sharedApp.homeViewController updateInboxCountLabel];
[self cancelNotificationsWithThreadId:threadId];
completionHandler();

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
@ -9,7 +9,7 @@ class ReminderView: UIView {
let TAG = "[ReminderView]"
let label = UILabel()
let defaultTapAction = {
static let defaultTapAction = {
Logger.debug("[ReminderView] tapped.")
}
@ -25,30 +25,51 @@ class ReminderView: UIView {
}
}
required init?(coder: NSCoder) {
self.tapAction = defaultTapAction
enum ReminderViewMode {
// Nags are urgent interactive prompts, bidding for the user's attention.
case nag
// Explanations are not interactive or urgent.
case explanation
}
let mode: ReminderViewMode
super.init(coder: coder)
setupSubviews()
@available(*, unavailable, message:"use other constructor instead.")
required init?(coder aDecoder: NSCoder) {
fatalError("\(#function) is unimplemented.")
}
@available(*, unavailable, message:"use other constructor instead.")
override init(frame: CGRect) {
self.tapAction = defaultTapAction
fatalError("\(#function) is unimplemented.")
}
super.init(frame: frame)
private init(mode: ReminderViewMode,
text: String, tapAction: @escaping () -> Void) {
self.mode = mode
self.tapAction = tapAction
super.init(frame: .zero)
self.text = text
setupSubviews()
}
convenience init(text: String, tapAction: @escaping () -> Void) {
self.init(frame: .zero)
self.text = text
self.tapAction = tapAction
@objc public class func nag(text: String, tapAction: @escaping () -> Void) -> ReminderView {
return ReminderView(mode: .nag, text: text, tapAction: tapAction)
}
@objc public class func explanation(text: String) -> ReminderView {
return ReminderView(mode: .explanation, text: text, tapAction: ReminderView.defaultTapAction)
}
func setupSubviews() {
self.backgroundColor = UIColor.ows_reminderYellow
switch (mode) {
case .nag:
self.backgroundColor = UIColor.ows_reminderYellow
case .explanation:
self.backgroundColor = UIColor(rgbHex: 0xf5f5f5)
}
self.clipsToBounds = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(gestureRecognizer:)))
@ -66,19 +87,25 @@ class ReminderView: UIView {
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.autoPinEdge(toSuperviewEdge: .top)
label.autoPinEdge(toSuperviewEdge: .left)
label.autoPinLeadingToSuperviewMargin()
label.autoPinEdge(toSuperviewEdge: .bottom)
label.textColor = UIColor.black.withAlphaComponent(0.9)
guard mode == .nag else {
label.autoPinTrailingToSuperviewMargin()
return
}
// Icon
let iconImage = #imageLiteral(resourceName: "system_disclosure_indicator").withRenderingMode(.alwaysTemplate)
let iconName = (self.isRTL() ? "system_disclosure_indicator_rtl" : "system_disclosure_indicator")
let iconImage = UIImage(named: iconName)?.withRenderingMode(.alwaysTemplate)
let iconView = UIImageView(image: iconImage)
iconView.contentMode = .scaleAspectFit
iconView.tintColor = UIColor.black.withAlphaComponent(0.6)
container.addSubview(iconView)
iconView.autoPinEdge(toSuperviewEdge: .right)
iconView.autoPinEdge(.left, to: .right, of: label, withOffset: 28)
iconView.autoPinLeading(toTrailingEdgeOf: label, offset: 28)
iconView.autoPinTrailingToSuperviewMargin()
iconView.autoVCenterInSuperview()
iconView.autoSetDimension(.width, toSize: 13)
}

View File

@ -76,9 +76,6 @@
/* Pressing this button moves a thread from the inbox to the archive */
"ARCHIVE_ACTION" = "Archive";
/* No comment provided by engineer. */
"ARCHIVE_NAV_BAR_TITLE" = "Archive";
/* No comment provided by engineer. */
"ATTACHMENT" = "Attachment";
@ -869,9 +866,18 @@
/* No comment provided by engineer. */
"GROUP_YOU_LEFT" = "You have left the group.";
/* Label for 'archived conversations' button. */
"HOME_VIEW_ARCHIVED_CONVERSATIONS" = "Archived Conversations";
/* A label for conversations with blocked users. */
"HOME_VIEW_BLOCKED_CONTACT_CONVERSATION" = "Blocked";
/* Title for the home view's 'archive' mode. */
"HOME_VIEW_TITLE_ARCHIVE" = "Archive";
/* Title for the home view's default mode. */
"HOME_VIEW_TITLE_INBOX" = "Signal";
/* Call setup status label */
"IN_CALL_CONNECTING" = "Connecting…";
@ -888,7 +894,7 @@
"IN_CALL_TERMINATED" = "Call Ended.";
/* Label reminding the user that they are in archive mode. */
"INBOX_VIEW_ARCHIVE_MODE_REMINDER" = "You are viewing your archived messages. Tap to return to your Inbox.";
"INBOX_VIEW_ARCHIVE_MODE_REMINDER" = "These conversations are archived. They will appear in the inbox if new messages are received.";
/* Multi-line label explaining how to show names instead of phone numbers in your inbox */
"INBOX_VIEW_MISSING_CONTACTS_PERMISSION" = "To see the names of your contacts, update your system settings to allow contact access.";
@ -2110,9 +2116,6 @@
/* Activity indicator title, shown upon returning to the device manager, until you complete the provisioning process on desktop */
"WAITING_TO_COMPLETE_DEVICE_LINK_TEXT" = "Complete setup on Signal Desktop.";
/* No comment provided by engineer. */
"WHISPER_NAV_BAR_TITLE" = "Inbox";
/* Info Message when you disable disappearing messages */
"YOU_DISABLED_DISAPPEARING_MESSAGES_CONFIGURATION" = "You disabled disappearing messages.";

View File

@ -3,7 +3,6 @@
//
#import "ViewControllerUtils.h"
#import "HomeViewController.h"
#import "NSString+OWS.h"
#import "PhoneNumber.h"
#import <AVFoundation/AVFoundation.h>