Merge branch 'charlesmchen/cvcLongPressBodyLinks'

This commit is contained in:
Matthew Chen 2021-06-17 10:04:22 -03:00
commit 3f91e966e3
11 changed files with 216 additions and 32 deletions

View File

@ -502,7 +502,7 @@ import SignalMessaging
var isUnknownCaller = false
if call.individualCall.direction == .incoming {
isUnknownCaller = !self.contactsManagerImpl.hasSignalAccount(for: call.individualCall.thread.contactAddress)
isUnknownCaller = !self.contactsManagerImpl.isSystemContactWithSignalAccount(call.individualCall.thread.contactAddress)
if isUnknownCaller {
Logger.warn("Using relay server because remote user is an unknown caller")
}
@ -763,7 +763,7 @@ import SignalMessaging
return
}
call.individualCall.isRemoteSharingScreen = false
case .reconnecting:
self.handleReconnecting(call: call)

View File

@ -68,8 +68,7 @@ extension ConversationViewController {
case .dataItem(let dataItem):
switch dataItem.dataType {
case .link:
// TODO: Show action sheet with options for links.
didTapLink(dataItem: dataItem)
didLongPressLink(dataItem: dataItem)
case .address:
// Open in iOS Maps app using URL.
//
@ -78,14 +77,7 @@ extension ConversationViewController {
// TODO: Show action sheet with options for addresses.
UIApplication.shared.open(dataItem.url, options: [:], completionHandler: nil)
case .phoneNumber:
// Initiate PSTN call using URL.
//
// https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/PhoneLinks/PhoneLinks.html
// https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/SMSLinks/SMSLinks.html
// https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/FacetimeLinks/FacetimeLinks.html
//
// TODO: Show action sheet with options for phone numbers.
UIApplication.shared.open(dataItem.url, options: [:], completionHandler: nil)
didLongPressPhoneNumber(dataItem: dataItem)
case .date:
// Open in iOS Calendar app using default URL.
//
@ -106,6 +98,141 @@ extension ConversationViewController {
}
}
// * URL
// * tap - open URL in safari
// * long press - preview + open link in safari / add to reading list / copy link / share
private func didLongPressLink(dataItem: CVBodyTextLabel.DataItem) {
let actionSheet = ActionSheetController(title: dataItem.snippet.strippedOrNil)
actionSheet.addAction(ActionSheetAction(title: NSLocalizedString("MESSAGE_ACTION_LINK_OPEN_LINK",
comment: "Label for button to open a link."),
accessibilityIdentifier: "link_open_link",
style: .default) { [weak self] _ in
self?.openLink(dataItem: dataItem)
})
actionSheet.addAction(ActionSheetAction(title: CommonStrings.copyButton,
accessibilityIdentifier: "link_copy",
style: .default) { _ in
UIPasteboard.general.string = dataItem.snippet
// TODO: Show toast?
})
actionSheet.addAction(ActionSheetAction(title: CommonStrings.shareButton,
accessibilityIdentifier: "link_share",
style: .default) { _ in
AttachmentSharing.showShareUI(for: dataItem.url, sender: self)
})
actionSheet.addAction(OWSActionSheets.cancelAction)
presentActionSheet(actionSheet)
}
// * phone number
// * tap - action sheet with call.
// * long press - show phone number + call PSTN / facetime audio / facetime video / send messages / add to contacts / copy
private func didLongPressPhoneNumber(dataItem: CVBodyTextLabel.DataItem) {
guard let snippet = dataItem.snippet.strippedOrNil,
let phoneNumber = PhoneNumber.tryParsePhoneNumber(fromUserSpecifiedText: snippet),
let e164 = phoneNumber.toE164().strippedOrNil else {
owsFailDebug("Invalid phone number.")
UIApplication.shared.open(dataItem.url, options: [:], completionHandler: nil)
return
}
let address = SignalServiceAddress(phoneNumber: e164)
if address.isLocalAddress ||
Self.contactsManagerImpl.isKnownRegisteredUserWithSneakyTransaction(address: address) {
let groupViewHelper: GroupViewHelper? = {
guard threadViewModel.isGroupThread else {
return nil
}
let groupViewHelper = GroupViewHelper(threadViewModel: threadViewModel)
groupViewHelper.delegate = self
return groupViewHelper
}()
let actionSheet = MemberActionSheet(address: address, groupViewHelper: groupViewHelper)
actionSheet.present(from: self)
return
}
let actionSheet = ActionSheetController(title: e164)
if address.isLocalAddress {
// Show no options.
} else if blockingManager.isAddressBlocked(address) {
actionSheet.addAction(ActionSheetAction(title: NSLocalizedString(
"BLOCK_LIST_UNBLOCK_BUTTON",
comment: "Button label for the 'unblock' button"
),
accessibilityIdentifier: "phone_number_unblock",
style: .default) { [weak self] _ in
guard let self = self else { return }
BlockListUIUtils.showUnblockAddressActionSheet(
address,
from: self,
completionBlock: nil
)
})
} else {
// https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/PhoneLinks/PhoneLinks.html
actionSheet.addAction(ActionSheetAction(title: NSLocalizedString("MESSAGE_ACTION_PHONE_NUMBER_CALL",
comment: "Label for button to call a phone number."),
accessibilityIdentifier: "phone_number_call",
style: .default) { _ in
guard let url = URL(string: "tel:" + e164) else {
owsFailDebug("Invalid phone number.")
return
}
UIApplication.shared.open(url, options: [:], completionHandler: nil)
})
// https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/SMSLinks/SMSLinks.html
actionSheet.addAction(ActionSheetAction(title: NSLocalizedString("MESSAGE_ACTION_PHONE_NUMBER_SMS",
comment: "Label for button to send a text message a phone number."),
accessibilityIdentifier: "phone_number_text_message",
style: .default) { _ in
guard let url = URL(string: "sms:" + e164) else {
owsFailDebug("Invalid phone number.")
return
}
UIApplication.shared.open(url, options: [:], completionHandler: nil)
})
// https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/FacetimeLinks/FacetimeLinks.html
actionSheet.addAction(ActionSheetAction(title: NSLocalizedString("MESSAGE_ACTION_PHONE_NUMBER_FACETIME_VIDEO",
comment: "Label for button to make a Facetime video call to a phone number."),
accessibilityIdentifier: "phone_number_facetime_video",
style: .default) { _ in
guard let url = URL(string: "facetime:" + e164) else {
owsFailDebug("Invalid phone number.")
return
}
UIApplication.shared.open(url, options: [:], completionHandler: nil)
})
actionSheet.addAction(ActionSheetAction(title: NSLocalizedString("MESSAGE_ACTION_PHONE_NUMBER_FACETIME_AUDIO",
comment: "Label for button to make a Facetime audio call to a phone number."),
accessibilityIdentifier: "phone_number_facetime_audio",
style: .default) { _ in
guard let url = URL(string: "facetime-audio:" + e164) else {
owsFailDebug("Invalid phone number.")
return
}
UIApplication.shared.open(url, options: [:], completionHandler: nil)
})
// TODO: We could show an "add to contact" action for this phone number.
// Ideally we could detect whether this phone number is already in a system contact.
// TODO: We could show an "share" action for this phone number.
}
actionSheet.addAction(ActionSheetAction(title: CommonStrings.copyButton,
accessibilityIdentifier: "phone_number_copy",
style: .default) { _ in
UIPasteboard.general.string = dataItem.snippet
// TODO: Show toast?
})
actionSheet.addAction(OWSActionSheets.cancelAction)
presentActionSheet(actionSheet)
}
private func didLongPressEmail(dataItem: CVBodyTextLabel.DataItem) {
let actionSheet = ActionSheetController(title: dataItem.snippet.strippedOrNil)
@ -129,6 +256,7 @@ extension ConversationViewController {
// TODO: We could show (Send Signal Message/Signal call) actions for this email address.
// Ideally we could detect whether this email address corresponds to a system contact
// which is a registered Signal user.
// TODO: We could show an "share" action for this email address.
actionSheet.addAction(OWSActionSheets.cancelAction)
@ -138,6 +266,12 @@ extension ConversationViewController {
private func didTapLink(dataItem: CVBodyTextLabel.DataItem) {
AssertIsOnMainThread()
openLink(dataItem: dataItem)
}
private func openLink(dataItem: CVBodyTextLabel.DataItem) {
AssertIsOnMainThread()
if isMailtoUrl(dataItem.url) {
didTapEmail(dataItem: dataItem)
} else {

View File

@ -1116,7 +1116,7 @@ typedef enum : NSUInteger {
}
// If the user is in the system contacts, show a badge
if ([self.contactsManagerImpl hasSignalAccountForAddress:thread.contactAddress]) {
if ([self.contactsManagerImpl isSystemContactWithAddress:thread.contactAddress]) {
icon =
[[UIImage imageNamed:@"contact-outline-16"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
}

View File

@ -21,12 +21,12 @@ extension ConversationSettingsViewController {
return !thread.isGroupThread
}
private var hasExistingContact: Bool {
private var hasExistingSystemContact: Bool {
guard let contactThread = thread as? TSContactThread else {
owsFailDebug("Invalid thread.")
return false
}
return contactsManagerImpl.hasSignalAccount(for: contactThread.contactAddress)
return contactsManagerImpl.isSystemContact(address: contactThread.contactAddress)
}
// MARK: - Table
@ -175,7 +175,7 @@ extension ConversationSettingsViewController {
let contactThread = thread as? TSContactThread,
contactsManagerImpl.supportsContactEditing else { return }
if hasExistingContact {
if hasExistingSystemContact {
section.add(OWSTableItem(customCellBlock: { [weak self] in
guard let self = self else {
owsFailDebug("Missing self")

View File

@ -439,6 +439,9 @@
/* Label for the 'set' button. */
"BUTTON_SET" = "Set";
/* Label for the 'share' button. */
"BUTTON_SHARE" = "Share";
/* Label for the 'start' button. */
"BUTTON_START" = "Start";
@ -2974,6 +2977,21 @@
/* Action sheet button title */
"MESSAGE_ACTION_FORWARD_MESSAGE" = "Forward This Message";
/* Label for button to open a link. */
"MESSAGE_ACTION_LINK_OPEN_LINK" = "Open Link";
/* Label for button to call a phone number. */
"MESSAGE_ACTION_PHONE_NUMBER_CALL" = "Call";
/* Label for button to make a Facetime audio call to a phone number. */
"MESSAGE_ACTION_PHONE_NUMBER_FACETIME_AUDIO" = "Facetime Audio";
/* Label for button to make a Facetime video call to a phone number. */
"MESSAGE_ACTION_PHONE_NUMBER_FACETIME_VIDEO" = "Facetime Video";
/* Label for button to send a text message a phone number. */
"MESSAGE_ACTION_PHONE_NUMBER_SMS" = "Send Text Message";
/* Action sheet button title */
"MESSAGE_ACTION_REPLY" = "Reply to This Message";

View File

@ -68,6 +68,9 @@ import Foundation
static public let saveButton = NSLocalizedString("ALERT_SAVE",
comment: "The label for the 'save' button in action sheets.")
@objc
static public let shareButton = NSLocalizedString("BUTTON_SHARE", comment: "Label for the 'share' button.")
@objc
static public let help = NSLocalizedString("SETTINGS_HELP", comment: "Title for help button and help pages in app settings.")

View File

@ -48,8 +48,6 @@ extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification;
// This will always return an instance of SignalAccount.
- (SignalAccount *)fetchOrBuildSignalAccountForAddress:(SignalServiceAddress *)address;
- (BOOL)hasSignalAccountForAddress:(SignalServiceAddress *)address;
- (BOOL)hasSignalAccountForAddress:(SignalServiceAddress *)address transaction:(SDSAnyReadTransaction *)transaction;
#pragma mark - System Contact Fetching
@ -100,6 +98,10 @@ extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification;
transaction:(SDSAnyReadTransaction *)transaction;
- (nullable UIImage *)imageForAddressWithSneakyTransaction:(nullable SignalServiceAddress *)address;
- (BOOL)isKnownRegisteredUserWithSneakyTransaction:(SignalServiceAddress *)address
NS_SWIFT_NAME(isKnownRegisteredUserWithSneakyTransaction(address:));
- (BOOL)isKnownRegisteredUser:(SignalServiceAddress *)address transaction:(SDSAnyReadTransaction *)transaction;
@end
NS_ASSUME_NONNULL_END

View File

@ -922,19 +922,19 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return [self isSystemContactWithPhoneNumber:phoneNumber];
}
- (BOOL)isSystemContactWithSignalAccount:(NSString *)phoneNumber
- (BOOL)isSystemContactWithSignalAccount:(SignalServiceAddress *)address
{
OWSAssertDebug(phoneNumber.length > 0);
OWSAssertDebug(address.isValid);
return [self hasSignalAccountForAddress:[[SignalServiceAddress alloc] initWithPhoneNumber:phoneNumber]];
return [self hasSignalAccountForAddress:address];
}
- (BOOL)isSystemContactWithSignalAccount:(NSString *)phoneNumber transaction:(SDSAnyReadTransaction *)transaction
- (BOOL)isSystemContactWithSignalAccount:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
{
OWSAssertDebug(phoneNumber.length > 0);
OWSAssertDebug(address.isValid);
return [self hasSignalAccountForAddress:[[SignalServiceAddress alloc] initWithPhoneNumber:phoneNumber]
transaction:transaction];
return [self hasSignalAccountForAddress:address transaction:transaction];
}
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address
@ -1358,6 +1358,19 @@ NSString *const OWSContactsManagerKeyNextFullIntersectionDate = @"OWSContactsMan
return [self displayNameForAddress:signalAccount.recipientAddress transaction:transaction];
}
NS_ASSUME_NONNULL_END
- (BOOL)isKnownRegisteredUserWithSneakyTransaction:(SignalServiceAddress *)address
{
__block BOOL result;
[self.databaseStorage readWithBlock:^(
SDSAnyReadTransaction *transaction) { result = [self isKnownRegisteredUser:address transaction:transaction]; }];
return result;
}
- (BOOL)isKnownRegisteredUser:(SignalServiceAddress *)address transaction:(SDSAnyReadTransaction *)transaction
{
return [SignalRecipient isRegisteredRecipient:address transaction:transaction];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -544,18 +544,20 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
- (NSArray<NSString *> *)systemContactsWithSignalAccountPhoneNumbers
{
return [self.e164PhoneNumbers
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable recipientId,
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable phoneNumber,
NSDictionary<NSString *, id> *_Nullable bindings) {
return [OWSContact.contactsManager isSystemContactWithSignalAccount:recipientId];
SignalServiceAddress *address = [[SignalServiceAddress alloc] initWithPhoneNumber:phoneNumber];
return [OWSContact.contactsManager isSystemContactWithSignalAccount:address];
}]];
}
- (NSArray<NSString *> *)systemContactsWithSignalAccountPhoneNumbersWithTransaction:(SDSAnyReadTransaction *)transaction
{
return [self.e164PhoneNumbers
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable recipientId,
filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSString *_Nullable phoneNumber,
NSDictionary<NSString *, id> *_Nullable bindings) {
return [OWSContact.contactsManager isSystemContactWithSignalAccount:recipientId transaction:transaction];
SignalServiceAddress *address = [[SignalServiceAddress alloc] initWithPhoneNumber:phoneNumber];
return [OWSContact.contactsManager isSystemContactWithSignalAccount:address transaction:transaction];
}]];
}

View File

@ -53,8 +53,11 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)isSystemContactWithPhoneNumber:(NSString *)phoneNumber NS_SWIFT_NAME(isSystemContact(phoneNumber:));
- (BOOL)isSystemContactWithAddress:(SignalServiceAddress *)address NS_SWIFT_NAME(isSystemContact(address:));
- (BOOL)isSystemContactWithSignalAccount:(NSString *)phoneNumber;
- (BOOL)isSystemContactWithSignalAccount:(NSString *)phoneNumber transaction:(SDSAnyReadTransaction *)transaction;
- (BOOL)isSystemContactWithSignalAccount:(SignalServiceAddress *)address
NS_SWIFT_NAME(isSystemContactWithSignalAccount(_:));
- (BOOL)isSystemContactWithSignalAccount:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction
NS_SWIFT_NAME(isSystemContactWithSignalAccount(_:transaction:));
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address;
- (BOOL)hasNameInSystemContactsForAddress:(SignalServiceAddress *)address
transaction:(SDSAnyReadTransaction *)transaction;

View File

@ -66,6 +66,15 @@ public class FakeContactsManager: NSObject, ContactsManagerProtocol {
return true
}
public func isSystemContactWithSignalAccount(_ address: SignalServiceAddress) -> Bool {
return true
}
public func isSystemContactWithSignalAccount(_ address: SignalServiceAddress,
transaction: SDSAnyReadTransaction) -> Bool {
return true
}
public func sortSignalServiceAddresses(_ addresses: [SignalServiceAddress],
transaction: SDSAnyReadTransaction) -> [SignalServiceAddress] {
return addresses