Add first conversation prompt.

This commit is contained in:
Matthew Chen 2019-02-19 09:52:10 -05:00
parent c4cc5f5744
commit d5944b4b9d
2 changed files with 147 additions and 56 deletions

View File

@ -71,7 +71,9 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
@property (nonatomic) UITableView *tableView;
@property (nonatomic) UIView *emptyInboxView;
@property (nonatomic) UIView *firstConversationCueView;
@property (nonatomic) UILabel *firstConversationLabel;
@property (nonatomic) YapDatabaseConnection *editingDbConnection;
@property (nonatomic) YapDatabaseConnection *uiDatabaseConnection;
@ -218,6 +220,10 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
OWSAssertIsOnMainThread();
[self reloadTableViewData];
if (!self.firstConversationCueView.isHidden) {
[self updateFirstConversationLabel];
}
}
- (void)registrationStateDidChange:(id)notification
@ -336,12 +342,15 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
[self.emptyInboxView autoPinWidthToSuperviewMargins];
[self.emptyInboxView autoVCenterInSuperview];
self.firstConversationCueView = [self createFirstConversationCueView];
[self createFirstConversationCueView];
[self.view addSubview:self.firstConversationCueView];
[self.firstConversationCueView autoPinEdgeToSuperviewEdge:ALEdgeTop];
[self.firstConversationCueView autoPinEdgeToSuperviewMargin:ALEdgeTrailing];
[self.emptyInboxView autoPinWidthToSuperviewMargins];
[self.emptyInboxView autoVCenterInSuperview];
[self.firstConversationCueView autoPinToTopLayoutGuideOfViewController:self withInset:0.f];
[self.firstConversationCueView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:10];
[self.firstConversationCueView autoPinEdgeToSuperviewEdge:ALEdgeLeading
withInset:10
relation:NSLayoutRelationGreaterThanOrEqual];
[self.firstConversationCueView autoPinEdgeToSuperviewMargin:ALEdgeBottom
relation:NSLayoutRelationGreaterThanOrEqual];
UIRefreshControl *pullToRefreshView = [UIRefreshControl new];
pullToRefreshView.tintColor = [UIColor grayColor];
@ -370,7 +379,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
UILabel *emptyInboxLabel = [UILabel new];
emptyInboxLabel.text = NSLocalizedString(@"INBOX_VIEW_EMPTY_INBOX",
@"Message shown in the home view when the inbox is empty.");
emptyInboxLabel.font = UIFont.ows_dynamicTypeBodyFont;
emptyInboxLabel.font = UIFont.ows_dynamicTypeBodyClampedFont;
emptyInboxLabel.textColor = Theme.secondaryColor;
emptyInboxLabel.textAlignment = NSTextAlignmentCenter;
emptyInboxLabel.numberOfLines = 0;
@ -388,41 +397,128 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
return emptyInboxStack;
}
- (UIView *)createFirstConversationCueView
- (void)createFirstConversationCueView
{
NSArray<NSString *> *emptyInboxImageNames = @[
@"home_empty_splash_1",
@"home_empty_splash_2",
@"home_empty_splash_3",
@"home_empty_splash_4",
@"home_empty_splash_5",
];
NSString *emptyInboxImageName = emptyInboxImageNames[arc4random_uniform((uint32_t)emptyInboxImageNames.count)];
UIImageView *emptyInboxImageView = [UIImageView new];
emptyInboxImageView.image = [UIImage imageNamed:emptyInboxImageName];
emptyInboxImageView.layer.minificationFilter = kCAFilterTrilinear;
emptyInboxImageView.layer.magnificationFilter = kCAFilterTrilinear;
[emptyInboxImageView autoPinToAspectRatioWithSize:emptyInboxImageView.image.size];
const CGFloat kTailWidth = 16.f;
const CGFloat kTailHeight = 8.f;
const CGFloat kTailHMargin = 12.f;
UILabel *emptyInboxLabel = [UILabel new];
emptyInboxLabel.text
= NSLocalizedString(@"INBOX_VIEW_EMPTY_INBOX", @"Message shown in the home view when the inbox is empty.");
emptyInboxLabel.font = UIFont.ows_dynamicTypeBodyFont;
emptyInboxLabel.textColor = Theme.secondaryColor;
emptyInboxLabel.textAlignment = NSTextAlignmentCenter;
emptyInboxLabel.numberOfLines = 0;
emptyInboxLabel.lineBreakMode = NSLineBreakByWordWrapping;
UILabel *label = [UILabel new];
label.textColor = UIColor.ows_whiteColor;
label.font = UIFont.ows_dynamicTypeBodyClampedFont;
label.numberOfLines = 0;
label.lineBreakMode = NSLineBreakByWordWrapping;
UIStackView *emptyInboxStack = [[UIStackView alloc] initWithArrangedSubviews:@[
emptyInboxImageView,
emptyInboxLabel,
]];
emptyInboxStack.axis = UILayoutConstraintAxisVertical;
emptyInboxStack.alignment = UIStackViewAlignmentCenter;
emptyInboxStack.spacing = 12;
emptyInboxStack.layoutMargins = UIEdgeInsetsMake(50, 50, 50, 50);
emptyInboxStack.layoutMarginsRelativeArrangement = YES;
return emptyInboxStack;
OWSLayerView *layerView = [OWSLayerView new];
layerView.layoutMargins = UIEdgeInsetsMake(11 + kTailHeight, 16, 7, 16);
CAShapeLayer *shapeLayer = [CAShapeLayer new];
shapeLayer.fillColor = [OWSConversationColor ows_wintergreenColor].CGColor;
[layerView.layer addSublayer:shapeLayer];
layerView.layoutCallback = ^(UIView *view) {
UIBezierPath *bezierPath = [UIBezierPath new];
// Bubble
CGRect bubbleBounds = view.bounds;
bubbleBounds.origin.y += kTailHeight;
bubbleBounds.size.height -= kTailHeight;
[bezierPath appendPath:[UIBezierPath bezierPathWithRoundedRect:bubbleBounds cornerRadius:8]];
// Tail
CGPoint tailTop = CGPointMake(kTailHMargin + kTailWidth * 0.5f, 0.f);
CGPoint tailLeft = CGPointMake(kTailHMargin, kTailHeight);
CGPoint tailRight = CGPointMake(kTailHMargin + kTailWidth, kTailHeight);
if (!CurrentAppContext().isRTL) {
tailTop.x = view.width - tailTop.x;
tailLeft.x = view.width - tailLeft.x;
tailRight.x = view.width - tailRight.x;
}
[bezierPath moveToPoint:tailTop];
[bezierPath addLineToPoint:tailLeft];
[bezierPath addLineToPoint:tailRight];
[bezierPath addLineToPoint:tailTop];
shapeLayer.path = bezierPath.CGPath;
shapeLayer.frame = view.bounds;
};
[layerView addSubview:label];
[label ows_autoPinToSuperviewMargins];
self.firstConversationCueView = layerView;
self.firstConversationLabel = label;
}
- (void)updateFirstConversationLabel
{
NSArray<SignalAccount *> *signalAccounts = self.contactsManager.signalAccounts;
NSString *formatString = @"";
NSMutableArray<NSString *> *contactNames = [NSMutableArray new];
if (signalAccounts.count >= 3) {
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[0]]];
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[1]]];
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[2]]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_3_CONTACTS_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have at least "
@"3 Signal contacts. Embeds {{The names of 3 of your Signal contacts}}.");
} else if (signalAccounts.count >= 2) {
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[0]]];
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[1]]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_2_CONTACTS_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have 2 Signal "
@"contacts. Embeds {{The names of 2 of your Signal contacts}}.");
} else if (signalAccounts.count >= 3) {
[contactNames addObject:[self.contactsManager displayNameForSignalAccount:signalAccounts[0]]];
formatString = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_1_CONTACT_FORMAT",
@"Format string for a label offering to start a new conversation with your contacts, if you have 1 Signal "
@"contact. Embeds {{The name of 1 of your Signal contacts}}.");
}
NSString *embedToken = @"%@";
NSArray<NSString *> *formatSplits = [formatString componentsSeparatedByString:embedToken];
// We need to use a complicated format string that possibly embeds multiple contact names.
// Translator error could easily lead to an invalid format string.
// We need to verify that it was translated properly.
BOOL isValidFormatString = (contactNames.count > 0 && formatSplits.count == contactNames.count + 1);
for (NSString *contactName in contactNames) {
if ([contactName containsString:embedToken]) {
isValidFormatString = NO;
}
}
NSMutableAttributedString *_Nullable attributedString = nil;
if (isValidFormatString) {
attributedString = [[NSMutableAttributedString alloc] initWithString:formatString];
while (contactNames.count > 0) {
NSString *contactName = contactNames.firstObject;
[contactNames removeObjectAtIndex:0];
NSRange range = [attributedString.string rangeOfString:embedToken];
if (range.location == NSNotFound) {
// Error
attributedString = nil;
break;
}
NSAttributedString *formattedName = [[NSAttributedString alloc]
initWithString:contactName
attributes:@{
NSFontAttributeName : self.firstConversationLabel.font.ows_mediumWeight,
}];
[attributedString replaceCharactersInRange:range withAttributedString:formattedName];
}
}
if (!attributedString) {
// The default case handles the no-contacts scenario and all error cases.
NSString *defaultText = NSLocalizedString(@"HOME_VIEW_FIRST_CONVERSATION_OFFER_NO_CONTACTS",
@"A label offering to start a new conversation with your contacts, if you have no Signal contacts.");
attributedString = [[NSMutableAttributedString alloc] initWithString:defaultText];
}
self.firstConversationLabel.attributedText = [attributedString copy];
}
- (void)updateReminderViews
@ -1524,6 +1620,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations
if (self.homeViewMode == HomeViewMode_Inbox && inboxCount == 0 && archiveCount == 0) {
[_tableView setHidden:YES];
[self.emptyInboxView setHidden:NO];
[self updateFirstConversationLabel];
} else {
[_tableView setHidden:NO];
[self.emptyInboxView setHidden:YES];

View File

@ -819,30 +819,12 @@
/* subject of email sent to contacts when inviting to install Signal */
"EMAIL_INVITE_SUBJECT" = "Let's switch to Signal";
/* Body text an existing user sees when viewing an empty archive */
"EMPTY_ARCHIVE_TEXT" = "You can archive inactive conversations from your Inbox.";
/* Header text an existing user sees when viewing an empty archive */
"EMPTY_ARCHIVE_TITLE" = "Clean Up Your Conversation List";
/* Full width label displayed when attempting to compose message */
"EMPTY_CONTACTS_LABEL_LINE1" = "None of your contacts have Signal.";
/* Full width label displayed when attempting to compose message */
"EMPTY_CONTACTS_LABEL_LINE2" = "Why don't you invite someone?";
/* Body text a new user sees when viewing an empty inbox */
"EMPTY_INBOX_NEW_USER_TEXT" = "Tap on the compose button";
/* Header text a new user sees when viewing an empty inbox */
"EMPTY_INBOX_NEW_USER_TITLE" = "Start your first Signal conversation!";
/* Body text an existing user sees when viewing an empty inbox */
"EMPTY_INBOX_TEXT" = "None. Zip. Zilch. Nada.";
/* Header text an existing user sees when viewing an empty inbox */
"EMPTY_INBOX_TITLE" = "Inbox Zero";
/* Indicates that user should confirm their 'two factor auth pin'. */
"ENABLE_2FA_VIEW_CONFIRM_PIN_INSTRUCTIONS" = "Confirm your PIN.";
@ -1074,6 +1056,18 @@
/* Placeholder text for search bar which filters conversations. */
"HOME_VIEW_CONVERSATION_SEARCHBAR_PLACEHOLDER" = "Search";
/* Format string for a label offering to start a new conversation with your contacts, if you have 1 Signal contact. Embeds: {{The name of 1 of your Signal contacts}}. */
"HOME_VIEW_FIRST_CONVERSATION_OFFER_1_CONTACT_FORMAT" = "Some of your contacts are already on Signal, including %@.";
/* Format string for a label offering to start a new conversation with your contacts, if you have 2 Signal contacts. Embeds: {{The names of 2 of your Signal contacts}}. */
"HOME_VIEW_FIRST_CONVERSATION_OFFER_2_CONTACTS_FORMAT" = "Some of your contacts are already on Signal, including %@ and %@";
/* Format string for a label offering to start a new conversation with your contacts, if you have at least 3 Signal contacts. Embeds: {{The names of 3 of your Signal contacts}}. */
"HOME_VIEW_FIRST_CONVERSATION_OFFER_3_CONTACTS_FORMAT" = "Some of your contacts are already on Signal, including %@, %@ and %@";
/* A label offering to start a new conversation with your contacts, if you have no Signal contacts. */
"HOME_VIEW_FIRST_CONVERSATION_OFFER_NO_CONTACTS" = "Start your first conversation here.";
/* Format string when search returns no results. Embeds {{search term}} */
"HOME_VIEW_SEARCH_NO_RESULTS_FORMAT" = "No results found for '%@'";