Add first conversation prompt.
This commit is contained in:
parent
c4cc5f5744
commit
d5944b4b9d
|
@ -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];
|
||||
|
|
|
@ -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 '%@'";
|
||||
|
||||
|
|
Loading…
Reference in New Issue