Merge branch 'mkirk/contact-share-avatars'
This commit is contained in:
commit
b7634c650c
@ -310,7 +310,8 @@
|
||||
45360B901F9527DA00FA666C /* SearcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45360B8F1F9527DA00FA666C /* SearcherTest.swift */; };
|
||||
45360B911F952AA900FA666C /* MarqueeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */; };
|
||||
4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4539B5851F79348F007141FF /* PushRegistrationManager.swift */; };
|
||||
4542DF52208B82E9007B4E76 /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadModel.swift */; };
|
||||
4541B71D209D3B7A0008608F /* ContactShareViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4541B71A209D2DAE0008608F /* ContactShareViewModel.swift */; };
|
||||
4542DF52208B82E9007B4E76 /* ThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadViewModel.swift */; };
|
||||
4542DF54208D40AC007B4E76 /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF53208D40AC007B4E76 /* LoadingViewController.swift */; };
|
||||
45464DBC1DFA041F001D3FD6 /* DataChannelMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */; };
|
||||
454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 454A84032059C787008B8C75 /* MediaTileViewController.swift */; };
|
||||
@ -939,7 +940,8 @@
|
||||
45360B8F1F9527DA00FA666C /* SearcherTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearcherTest.swift; sourceTree = "<group>"; };
|
||||
4539B5851F79348F007141FF /* PushRegistrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushRegistrationManager.swift; sourceTree = "<group>"; };
|
||||
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
4542DF51208B82E9007B4E76 /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
|
||||
4541B71A209D2DAE0008608F /* ContactShareViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactShareViewModel.swift; sourceTree = "<group>"; };
|
||||
4542DF51208B82E9007B4E76 /* ThreadViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadViewModel.swift; sourceTree = "<group>"; };
|
||||
4542DF53208D40AC007B4E76 /* LoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewController.swift; sourceTree = "<group>"; };
|
||||
45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataChannelMessage.swift; sourceTree = "<group>"; };
|
||||
454A84032059C787008B8C75 /* MediaTileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaTileViewController.swift; sourceTree = "<group>"; };
|
||||
@ -1806,6 +1808,7 @@
|
||||
450DF2061E0DD28D003D14BE /* UserInterface */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4541B719209D2D860008608F /* ViewModels */,
|
||||
450DF2071E0DD29E003D14BE /* Notifications */,
|
||||
34FD936E1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.h */,
|
||||
34FD936F1E3BD43A00109093 /* OWSAnyTouchGestureRecognizer.m */,
|
||||
@ -1827,8 +1830,6 @@
|
||||
45194F911FD7214600333B2C /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
459B7759207BA3A80071D0AB /* OWSQuotedReplyModel.h */,
|
||||
459B775A207BA3A80071D0AB /* OWSQuotedReplyModel.m */,
|
||||
34C42D621F4734ED0072EC04 /* OWSContactOffersInteraction.h */,
|
||||
34C42D631F4734ED0072EC04 /* OWSContactOffersInteraction.m */,
|
||||
34C42D641F4734ED0072EC04 /* TSUnreadIndicatorInteraction.h */,
|
||||
@ -1869,6 +1870,7 @@
|
||||
453518931FC63DBF00210559 /* SignalMessaging */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4541B71C209D3B4F0008608F /* ViewModels */,
|
||||
45194F911FD7214600333B2C /* Models */,
|
||||
451F8A361FD7115D005CB9DA /* ViewControllers */,
|
||||
454A96571FD600B4008D2A0E /* attachments */,
|
||||
@ -1886,6 +1888,24 @@
|
||||
path = SignalMessaging;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4541B719209D2D860008608F /* ViewModels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4542DF51208B82E9007B4E76 /* ThreadViewModel.swift */,
|
||||
);
|
||||
path = ViewModels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4541B71C209D3B4F0008608F /* ViewModels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4541B71A209D2DAE0008608F /* ContactShareViewModel.swift */,
|
||||
459B7759207BA3A80071D0AB /* OWSQuotedReplyModel.h */,
|
||||
459B775A207BA3A80071D0AB /* OWSQuotedReplyModel.m */,
|
||||
);
|
||||
path = ViewModels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
45464DB81DFA03D8001D3FD6 /* Signaling */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1931,7 +1951,6 @@
|
||||
45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */,
|
||||
458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */,
|
||||
458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */,
|
||||
4542DF51208B82E9007B4E76 /* ThreadModel.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@ -3107,6 +3126,7 @@
|
||||
4503F1C3204711D300CEE724 /* OWS107LegacySounds.m in Sources */,
|
||||
3438226A209B63500094FEB7 /* EditContactShareNameViewController.swift in Sources */,
|
||||
346129A61FD1F09100532771 /* OWSContactsManager.m in Sources */,
|
||||
4541B71D209D3B7A0008608F /* ContactShareViewModel.swift in Sources */,
|
||||
4598198F204E2F28009414F2 /* OWS108CallLoggingPreference.m in Sources */,
|
||||
346129D21FD2085A00532771 /* CommonStrings.swift in Sources */,
|
||||
45F59A082028E4FB00E8D2B0 /* OWSAudioSession.swift in Sources */,
|
||||
@ -3147,7 +3167,7 @@
|
||||
34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */,
|
||||
34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */,
|
||||
340FC8B8204DAC8D007AEB0F /* AddToGroupViewController.m in Sources */,
|
||||
4542DF52208B82E9007B4E76 /* ThreadModel.swift in Sources */,
|
||||
4542DF52208B82E9007B4E76 /* ThreadViewModel.swift in Sources */,
|
||||
341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */,
|
||||
340FC8AF204DAC8D007AEB0F /* OWSLinkDeviceViewController.m in Sources */,
|
||||
34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */,
|
||||
|
||||
@ -65,7 +65,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
|
||||
var reachability: Reachability?
|
||||
|
||||
private let contact: OWSContact
|
||||
private let contactShare: ContactShareViewModel
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
@ -74,9 +74,9 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
fatalError("Unimplemented")
|
||||
}
|
||||
|
||||
required init(contact: OWSContact) {
|
||||
required init(contactShare: ContactShareViewModel) {
|
||||
contactsManager = Environment.current().contactsManager
|
||||
self.contact = contact
|
||||
self.contactShare = contactShare
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
@ -140,7 +140,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
private func updateMode() {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
guard phoneNumbersForContact().count > 0 else {
|
||||
guard contactShare.phoneNumberStrings.count > 0 else {
|
||||
viewMode = .noPhoneNumber
|
||||
return
|
||||
}
|
||||
@ -159,7 +159,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
private func systemContactsWithSignalAccountsForContact() -> [String] {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
return phoneNumbersForContact().filter({ (phoneNumber) -> Bool in
|
||||
return contactShare.phoneNumberStrings.filter({ (phoneNumber) -> Bool in
|
||||
return contactsManager.hasSignalAccount(forRecipientId: phoneNumber)
|
||||
})
|
||||
}
|
||||
@ -167,21 +167,11 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
private func systemContactsForContact() -> [String] {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
return phoneNumbersForContact().filter({ (phoneNumber) -> Bool in
|
||||
return contactShare.phoneNumberStrings.filter({ (phoneNumber) -> Bool in
|
||||
return contactsManager.allContactsMap[phoneNumber] != nil
|
||||
})
|
||||
}
|
||||
|
||||
private func phoneNumbersForContact() -> [String] {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
var result = [String]()
|
||||
for phoneNumber in contact.phoneNumbers {
|
||||
result.append(phoneNumber.phoneNumber)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func updateContent() {
|
||||
SwiftAssertIsOnMainThread(#function)
|
||||
|
||||
@ -259,16 +249,9 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
backButton.addSubview(backIconView)
|
||||
backIconView.autoCenterInSuperview()
|
||||
|
||||
// TODO: Use actual avatar.
|
||||
let avatarSize = CGFloat(100)
|
||||
|
||||
let avatarSize: CGFloat = 100
|
||||
let avatarView = AvatarImageView()
|
||||
// TODO: What's the best colorSeed value to use?
|
||||
let avatarBuilder = OWSContactAvatarBuilder(nonSignalName: contact.displayName,
|
||||
colorSeed: contact.displayName,
|
||||
diameter: UInt(avatarSize),
|
||||
contactsManager: contactsManager)
|
||||
avatarView.image = avatarBuilder.build()
|
||||
avatarView.image = contactShare.getAvatarImage(diameter: avatarSize, contactsManager: contactsManager)
|
||||
topView.addSubview(avatarView)
|
||||
avatarView.autoPin(toTopLayoutGuideOf: self, withInset: 20)
|
||||
avatarView.autoHCenterInSuperview()
|
||||
@ -276,7 +259,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
avatarView.autoSetDimension(.height, toSize: avatarSize)
|
||||
|
||||
let nameLabel = UILabel()
|
||||
nameLabel.text = contact.displayName
|
||||
nameLabel.text = contactShare.displayName
|
||||
nameLabel.font = UIFont.ows_dynamicTypeTitle2.ows_bold()
|
||||
nameLabel.textColor = UIColor.black
|
||||
nameLabel.lineBreakMode = .byTruncatingTail
|
||||
@ -288,7 +271,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
|
||||
var lastView: UIView = nameLabel
|
||||
|
||||
if let firstPhoneNumber = contact.phoneNumbers.first {
|
||||
if let firstPhoneNumber = contactShare.phoneNumbers.first {
|
||||
let phoneNumberLabel = UILabel()
|
||||
phoneNumberLabel.text = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: firstPhoneNumber.phoneNumber)
|
||||
phoneNumberLabel.font = UIFont.ows_dynamicTypeCaption2
|
||||
@ -387,7 +370,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
// action:#selector(didPressShareContact)))
|
||||
// }
|
||||
|
||||
for phoneNumber in contact.phoneNumbers {
|
||||
for phoneNumber in contactShare.phoneNumbers {
|
||||
let formattedPhoneNumber = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: phoneNumber.phoneNumber)
|
||||
|
||||
rows.append(createNameValueRow(name: phoneNumber.localizedLabel(),
|
||||
@ -401,7 +384,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
}))
|
||||
}
|
||||
|
||||
for email in contact.emails {
|
||||
for email in contactShare.emails {
|
||||
rows.append(createNameValueRow(name: email.localizedLabel(),
|
||||
value: email.email,
|
||||
actionBlock: {
|
||||
@ -599,7 +582,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
OWSAlerts.showErrorAlert(message: NSLocalizedString("UNSUPPORTED_FEATURE_ERROR", comment: ""))
|
||||
return
|
||||
}
|
||||
let phoneNumbers = phoneNumbersForContact()
|
||||
let phoneNumbers = contactShare.phoneNumberStrings
|
||||
guard phoneNumbers.count > 0 else {
|
||||
owsFail("\(logTag) no phone numbers.")
|
||||
return
|
||||
@ -639,8 +622,8 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
return
|
||||
}
|
||||
|
||||
guard let systemContact = OWSContacts.systemContact(for: contact) else {
|
||||
owsFail("\(logTag) Could not derive system contact.")
|
||||
guard let systemContact = OWSContacts.systemContact(for: contactShare.dbRecord) else {
|
||||
owsFail("\(logTag) Could not derive system contactShare.")
|
||||
return
|
||||
}
|
||||
|
||||
@ -678,7 +661,7 @@ class ContactViewController: OWSViewController, CNContactViewControllerDelegate
|
||||
return
|
||||
}
|
||||
|
||||
guard let firstPhoneNumber = contact.phoneNumbers.first else {
|
||||
guard let firstPhoneNumber = contactShare.phoneNumbers.first else {
|
||||
owsFail("\(logTag) Missing phone number.")
|
||||
return
|
||||
}
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSContact;
|
||||
@class ContactShareViewModel;
|
||||
|
||||
@interface OWSContactShareView : UIView
|
||||
|
||||
- (instancetype)initWithContactShare:(OWSContact *)contactShare isIncoming:(BOOL)isIncoming;
|
||||
- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare isIncoming:(BOOL)isIncoming;
|
||||
|
||||
- (void)createContents;
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSContactShareView ()
|
||||
|
||||
@property (nonatomic) OWSContact *contactShare;
|
||||
@property (nonatomic) ContactShareViewModel *contactShare;
|
||||
@property (nonatomic) BOOL isIncoming;
|
||||
|
||||
@end
|
||||
@ -26,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSContactShareView
|
||||
|
||||
- (instancetype)initWithContactShare:(OWSContact *)contactShare isIncoming:(BOOL)isIncoming
|
||||
- (instancetype)initWithContactShare:(ContactShareViewModel *)contactShare isIncoming:(BOOL)isIncoming
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
@ -38,6 +38,11 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return self;
|
||||
}
|
||||
|
||||
- (OWSContactsManager *)contactsManager
|
||||
{
|
||||
return [Environment current].contactsManager;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (CGFloat)iconHMargin
|
||||
@ -105,15 +110,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[contentView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:self.vMargin];
|
||||
[contentView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:self.vMargin];
|
||||
|
||||
// TODO: Use the contact's avatar if present and downloaded.
|
||||
AvatarImageView *avatarView = [AvatarImageView new];
|
||||
// TODO: What's the best colorSeed value to use?
|
||||
OWSAvatarBuilder *avatarBuilder =
|
||||
[[OWSContactAvatarBuilder alloc] initWithNonSignalName:self.contactShare.displayName
|
||||
colorSeed:self.contactShare.displayName
|
||||
diameter:(NSUInteger)self.iconSize
|
||||
contactsManager:[Environment current].contactsManager];
|
||||
avatarView.image = [avatarBuilder build];
|
||||
avatarView.image =
|
||||
[self.contactShare getAvatarImageWithDiameter:self.iconSize contactsManager:self.contactsManager];
|
||||
|
||||
[avatarView autoSetDimension:ALDimensionWidth toSize:self.iconSize];
|
||||
[avatarView autoSetDimension:ALDimensionHeight toSize:self.iconSize];
|
||||
[avatarView setCompressionResistanceHigh];
|
||||
|
||||
@ -65,6 +65,7 @@
|
||||
#import <SignalMessaging/ThreadUtil.h>
|
||||
#import <SignalMessaging/UIUtil.h>
|
||||
#import <SignalMessaging/UIViewController+OWS.h>
|
||||
#import <SignalServiceKit/Contact.h>
|
||||
#import <SignalServiceKit/ContactsUpdater.h>
|
||||
#import <SignalServiceKit/MimeTypeUtil.h>
|
||||
#import <SignalServiceKit/NSDate+OWS.h>
|
||||
@ -2087,7 +2088,7 @@ typedef enum : NSUInteger {
|
||||
OWSAssert(conversationItem.contactShare);
|
||||
OWSAssert([conversationItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
ContactViewController *view = [[ContactViewController alloc] initWithContact:conversationItem.contactShare];
|
||||
ContactViewController *view = [[ContactViewController alloc] initWithContactShare:conversationItem.contactShare];
|
||||
[self.navigationController pushViewController:view animated:YES];
|
||||
}
|
||||
|
||||
@ -2948,7 +2949,7 @@ typedef enum : NSUInteger {
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendContactShare:(OWSContact *)contactShare
|
||||
- (void)sendContactShare:(ContactShareViewModel *)contactShare
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssert(contactShare);
|
||||
@ -2956,16 +2957,23 @@ typedef enum : NSUInteger {
|
||||
DDLogVerbose(@"%@ Sending contact share.", self.logTag);
|
||||
|
||||
BOOL didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyContactThread:self.thread];
|
||||
TSOutgoingMessage *message = [ThreadUtil sendMessageWithContactShare:contactShare
|
||||
inThread:self.thread
|
||||
messageSender:self.messageSender
|
||||
completion:nil];
|
||||
|
||||
[self messageWasSent:message];
|
||||
|
||||
if (didAddToProfileWhitelist) {
|
||||
[self ensureDynamicInteractions];
|
||||
[self.editingDatabaseConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
||||
if (contactShare.avatarImage) {
|
||||
[contactShare.dbRecord saveAvatarImage:contactShare.avatarImage transaction:transaction];
|
||||
}
|
||||
}
|
||||
completionBlock:^{
|
||||
TSOutgoingMessage *message = [ThreadUtil sendMessageWithContactShare:contactShare.dbRecord
|
||||
inThread:self.thread
|
||||
messageSender:self.messageSender
|
||||
completion:nil];
|
||||
[self messageWasSent:message];
|
||||
|
||||
if (didAddToProfileWhitelist) {
|
||||
[self ensureDynamicInteractions];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSURL *)videoTempFolder
|
||||
@ -4942,12 +4950,28 @@ interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransiti
|
||||
|
||||
DDLogDebug(@"%@ in %s with contact: %@", self.logTag, __PRETTY_FUNCTION__, contact);
|
||||
|
||||
OWSContact *_Nullable contactShare = [OWSContacts contactForSystemContact:contact.cnContact];
|
||||
if (!contactShare) {
|
||||
OWSContact *_Nullable contactShareRecord = [OWSContacts contactForSystemContact:contact.cnContact];
|
||||
if (!contactShareRecord) {
|
||||
DDLogError(@"%@ Could not convert system contact.", self.logTag);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL isProfileAvatar = NO;
|
||||
UIImage *_Nullable avatarImage = contact.image;
|
||||
if (!avatarImage) {
|
||||
NSString *firstSignalId = contact.textSecureIdentifiers.firstObject;
|
||||
if (firstSignalId) {
|
||||
avatarImage = [self.contactsManager profileImageForPhoneIdentifier:firstSignalId];
|
||||
if (avatarImage) {
|
||||
isProfileAvatar = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ContactShareViewModel *contactShare =
|
||||
[[ContactShareViewModel alloc] initWithContactShareRecord:contactShareRecord avatarImage:avatarImage];
|
||||
contactShareRecord.isProfileAvatar = isProfileAvatar;
|
||||
|
||||
// TODO: We should probably show this in the same navigation view controller.
|
||||
ApproveContactShareViewController *approveContactShare =
|
||||
[[ApproveContactShareViewController alloc] initWithContactShare:contactShare
|
||||
@ -4974,7 +4998,7 @@ interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransiti
|
||||
#pragma mark - ApproveContactShareViewControllerDelegate
|
||||
|
||||
- (void)approveContactShare:(ApproveContactShareViewController *)approveContactShare
|
||||
didApproveContactShare:(OWSContact *)contactShare
|
||||
didApproveContactShare:(ContactShareViewModel *)contactShare
|
||||
{
|
||||
DDLogInfo(@"%@ in %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
@ -4985,7 +5009,7 @@ interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransiti
|
||||
}
|
||||
|
||||
- (void)approveContactShare:(ApproveContactShareViewController *)approveContactShare
|
||||
didCancelContactShare:(OWSContact *)contactShare
|
||||
didCancelContactShare:(ContactShareViewModel *)contactShare
|
||||
{
|
||||
DDLogInfo(@"%@ in %s", self.logTag, __PRETTY_FUNCTION__);
|
||||
|
||||
|
||||
@ -24,10 +24,10 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@class ContactShareViewModel;
|
||||
@class ConversationViewCell;
|
||||
@class DisplayableText;
|
||||
@class OWSAudioMessageView;
|
||||
@class OWSContact;
|
||||
@class OWSQuotedReplyModel;
|
||||
@class TSAttachmentPointer;
|
||||
@class TSAttachmentStream;
|
||||
@ -103,7 +103,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType);
|
||||
// if a load has previously failed.
|
||||
@property (nonatomic) BOOL didCellMediaFailToLoad;
|
||||
|
||||
- (nullable OWSContact *)contactShare;
|
||||
@property (nonatomic, readonly, nullable) ContactShareViewModel *contactShare;
|
||||
|
||||
#pragma mark - UIMenuController
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
||||
@property (nonatomic, readonly, nullable) NSString *quotedRecipientId;
|
||||
@property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
|
||||
@property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer;
|
||||
@property (nonatomic, nullable) OWSContact *contactShare;
|
||||
@property (nonatomic, nullable) ContactShareViewModel *contactShare;
|
||||
@property (nonatomic) CGSize mediaSize;
|
||||
|
||||
@end
|
||||
@ -406,7 +406,8 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
||||
|
||||
TSMessage *message = (TSMessage *)self.interaction;
|
||||
if (message.contactShare) {
|
||||
self.contactShare = message.contactShare;
|
||||
self.contactShare =
|
||||
[[ContactShareViewModel alloc] initWithContactShareRecord:message.contactShare transaction:transaction];
|
||||
self.messageCellType = OWSMessageCellType_ContactShare;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -84,8 +84,12 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
actionBlock:^{
|
||||
[DebugUIMessages deleteAllMessagesInThread:thread];
|
||||
}]];
|
||||
|
||||
[items addObject:[OWSTableItem itemWithTitle:@"👷 Send All Contact Shares"
|
||||
actionBlock:^{
|
||||
[DebugUIMessages sendAllContacts:thread];
|
||||
}]];
|
||||
[items addObjectsFromArray:[self itemsForActions:@[
|
||||
[DebugUIMessages fakeAllContactShareAction:thread],
|
||||
[DebugUIMessages sendMessageVariationsAction:thread],
|
||||
// Send Media
|
||||
[DebugUIMessages sendAllMediaAction:thread],
|
||||
@ -103,7 +107,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// Exemplary
|
||||
[DebugUIMessages allFakeAction:thread],
|
||||
[DebugUIMessages allFakeBackDatedAction:thread],
|
||||
[DebugUIMessages allContactShareAction:thread],
|
||||
]]];
|
||||
|
||||
[items addObjectsFromArray:@[
|
||||
@ -130,10 +133,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
actionBlock:^{
|
||||
[DebugUIMessages selectBackDatedAction:thread];
|
||||
}],
|
||||
[OWSTableItem itemWithTitle:@"Send All Contact Shares"
|
||||
actionBlock:^{
|
||||
[DebugUIMessages sendAllContacts:thread];
|
||||
}],
|
||||
|
||||
|
||||
#pragma mark - Misc.
|
||||
|
||||
@ -2914,7 +2914,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Contact Shares
|
||||
|
||||
typedef OWSContact * (^OWSContactBlock)(void);
|
||||
typedef OWSContact * (^OWSContactBlock)(YapDatabaseReadWriteTransaction *transaction);
|
||||
|
||||
+ (DebugUIMessagesAction *)fakeContactShareMessageAction:(TSThread *)thread
|
||||
label:(NSString *)label
|
||||
@ -2925,7 +2925,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
return [DebugUIMessagesSingleAction
|
||||
actionWithLabel:[NSString stringWithFormat:@"Fake Contact Share (%@)", label]
|
||||
unstaggeredActionBlock:^(NSUInteger index, YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSContact *contact = contactBlock();
|
||||
OWSContact *contact = contactBlock(transaction);
|
||||
TSOutgoingMessage *message = [self createFakeOutgoingMessage:thread
|
||||
messageBody:nil
|
||||
fakeAssetLoader:nil
|
||||
@ -2953,7 +2953,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
|
||||
[actions addObject:[self fakeContactShareMessageAction:thread
|
||||
label:@"Name & Number"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction){
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Alice";
|
||||
OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
|
||||
@ -2966,7 +2966,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
}]];
|
||||
[actions addObject:[self fakeContactShareMessageAction:thread
|
||||
label:@"Name & Email"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction){
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Bob";
|
||||
OWSContactEmail *email = [OWSContactEmail new];
|
||||
@ -2979,7 +2979,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
}]];
|
||||
[actions addObject:[self fakeContactShareMessageAction:thread
|
||||
label:@"Complicated"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction){
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Alice";
|
||||
contact.familyName = @"Carol";
|
||||
@ -3031,12 +3031,15 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
address2,
|
||||
];
|
||||
|
||||
// TODO: Avatar
|
||||
UIImage *avatarImage =
|
||||
[OWSAvatarBuilder buildRandomAvatarWithDiameter:200];
|
||||
[contact saveAvatarImage:avatarImage transaction:transaction];
|
||||
|
||||
return contact;
|
||||
}]];
|
||||
[actions addObject:[self fakeContactShareMessageAction:thread
|
||||
label:@"Long values"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction){
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Bobasdjasdlkjasldkjas";
|
||||
contact.familyName = @"Bobasdjasdlkjasldkjas";
|
||||
@ -3050,7 +3053,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
}]];
|
||||
[actions addObject:[self fakeContactShareMessageAction:thread
|
||||
label:@"System Contact w/o Signal"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction){
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Add Me To Your Contacts";
|
||||
OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
|
||||
@ -3063,7 +3066,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
}]];
|
||||
[actions addObject:[self fakeContactShareMessageAction:thread
|
||||
label:@"System Contact w. Signal"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction){
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Add Me To Your Contacts";
|
||||
OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
|
||||
@ -3078,12 +3081,12 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
return actions;
|
||||
}
|
||||
|
||||
+ (DebugUIMessagesAction *)allContactShareAction:(TSThread *)thread
|
||||
+ (DebugUIMessagesAction *)fakeAllContactShareAction:(TSThread *)thread
|
||||
{
|
||||
OWSAssert(thread);
|
||||
|
||||
return
|
||||
[DebugUIMessagesGroupAction allGroupActionWithLabel:@"All Fake Contact Shares"
|
||||
[DebugUIMessagesGroupAction allGroupActionWithLabel:@"👷 All Fake Contact Shares"
|
||||
subactions:[self allFakeContactShareActions:thread includeLabels:YES]];
|
||||
}
|
||||
|
||||
@ -3094,24 +3097,19 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
{
|
||||
OWSAssert(thread);
|
||||
|
||||
return
|
||||
[DebugUIMessagesSingleAction actionWithLabel:[NSString stringWithFormat:@"Send Contact Share (%@)", label]
|
||||
staggeredActionBlock:^(NSUInteger index,
|
||||
YapDatabaseReadWriteTransaction *transaction,
|
||||
ActionSuccessBlock success,
|
||||
ActionFailureBlock failure) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
OWSContact *contact = contactBlock();
|
||||
DDLogVerbose(@"%@ sending contact: %@", self.logTag, contact.debugDescription);
|
||||
OWSMessageSender *messageSender = [Environment current].messageSender;
|
||||
[ThreadUtil sendMessageWithContactShare:contact
|
||||
inThread:thread
|
||||
messageSender:messageSender
|
||||
completion:nil];
|
||||
return [DebugUIMessagesSingleAction
|
||||
actionWithLabel:[NSString stringWithFormat:@"Send Contact Share (%@)", label]
|
||||
staggeredActionBlock:^(NSUInteger index,
|
||||
YapDatabaseReadWriteTransaction *transaction,
|
||||
ActionSuccessBlock success,
|
||||
ActionFailureBlock failure) {
|
||||
OWSContact *contact = contactBlock(transaction);
|
||||
DDLogVerbose(@"%@ sending contact: %@", self.logTag, contact.debugDescription);
|
||||
OWSMessageSender *messageSender = [Environment current].messageSender;
|
||||
[ThreadUtil sendMessageWithContactShare:contact inThread:thread messageSender:messageSender completion:nil];
|
||||
|
||||
success();
|
||||
});
|
||||
}];
|
||||
success();
|
||||
}];
|
||||
}
|
||||
|
||||
+ (NSArray<DebugUIMessagesAction *> *)allSendContactShareActions:(TSThread *)thread includeLabels:(BOOL)includeLabels
|
||||
@ -3128,7 +3126,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
|
||||
[actions addObject:[self sendContactShareMessageAction:thread
|
||||
label:@"Name & Number"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Alice";
|
||||
OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
|
||||
@ -3141,7 +3139,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
}]];
|
||||
[actions addObject:[self sendContactShareMessageAction:thread
|
||||
label:@"Name & Email"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Bob";
|
||||
OWSContactEmail *email = [OWSContactEmail new];
|
||||
@ -3154,7 +3152,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
}]];
|
||||
[actions addObject:[self sendContactShareMessageAction:thread
|
||||
label:@"Complicated"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction){
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Alice";
|
||||
contact.familyName = @"Carol";
|
||||
@ -3206,12 +3204,15 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
address2,
|
||||
];
|
||||
|
||||
// TODO: Avatar
|
||||
UIImage *avatarImage =
|
||||
[OWSAvatarBuilder buildRandomAvatarWithDiameter:200];
|
||||
[contact saveAvatarImage:avatarImage transaction:transaction];
|
||||
|
||||
return contact;
|
||||
}]];
|
||||
[actions addObject:[self sendContactShareMessageAction:thread
|
||||
label:@"Long values"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Bobasdjasdlkjasldkjas";
|
||||
contact.familyName = @"Bobasdjasdlkjasldkjas";
|
||||
@ -3225,7 +3226,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
}]];
|
||||
[actions addObject:[self sendContactShareMessageAction:thread
|
||||
label:@"System Contact w/o Signal"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Add Me To Your Contacts";
|
||||
OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
|
||||
@ -3238,7 +3239,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
}]];
|
||||
[actions addObject:[self sendContactShareMessageAction:thread
|
||||
label:@"System Contact w. Signal"
|
||||
contactBlock:^{
|
||||
contactBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
OWSContact *contact = [OWSContact new];
|
||||
contact.givenName = @"Add Me To Your Contacts";
|
||||
OWSContactPhoneNumber *phoneNumber = [OWSContactPhoneNumber new];
|
||||
@ -3583,9 +3584,9 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
|
||||
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:phoneNumber.toE164];
|
||||
[self sendFakeMessages:messageCount thread:contactThread];
|
||||
DDLogError(@"Create fake thread: %@, interactions: %tu",
|
||||
DDLogError(@"Create fake thread: %@, interactions: %lu",
|
||||
phoneNumber.toE164,
|
||||
contactThread.numberOfInteractions);
|
||||
(unsigned long)contactThread.numberOfInteractions);
|
||||
}];
|
||||
}
|
||||
|
||||
@ -3611,7 +3612,10 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
[self sendFakeMessages:batchSize thread:thread isTextOnly:isTextOnly transaction:transaction];
|
||||
}];
|
||||
remainder -= batchSize;
|
||||
DDLogInfo(@"%@ sendFakeMessages %td / %tu", self.logTag, counter - remainder, counter);
|
||||
DDLogInfo(@"%@ sendFakeMessages %lu / %lu",
|
||||
self.logTag,
|
||||
(unsigned long)(counter - remainder),
|
||||
(unsigned long)counter);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -3623,7 +3627,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
isTextOnly:(BOOL)isTextOnly
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
DDLogInfo(@"%@ sendFakeMessages: %tu", self.logTag, counter);
|
||||
DDLogInfo(@"%@ sendFakeMessages: %lu", self.logTag, (unsigned long)counter);
|
||||
|
||||
for (NSUInteger i = 0; i < counter; i++) {
|
||||
NSString *randomText = [self randomText];
|
||||
@ -3775,7 +3779,7 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
{
|
||||
OWSAssert(thread);
|
||||
|
||||
DDLogInfo(@"%@ injectIncomingMessageInThread: %tu", self.logTag, counter);
|
||||
DDLogInfo(@"%@ injectIncomingMessageInThread: %lu", self.logTag, (unsigned long)counter);
|
||||
|
||||
NSString *randomText = [self randomText];
|
||||
NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText];
|
||||
@ -4167,7 +4171,8 @@ typedef OWSContact * (^OWSContactBlock)(void);
|
||||
inThread:thread
|
||||
authorId:thread.recipientIdentifiers.firstObject
|
||||
sourceDeviceId:0
|
||||
messageBody:[NSString stringWithFormat:@"Should disappear 60s after %tu", now]
|
||||
messageBody:[NSString
|
||||
stringWithFormat:@"Should disappear 60s after %lu", (unsigned long)now]
|
||||
attachmentIds:[NSMutableArray new]
|
||||
expiresInSeconds:60
|
||||
quotedMessage:nil
|
||||
|
||||
@ -613,11 +613,11 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele
|
||||
}
|
||||
|
||||
func didTapContactShare(_ viewItem: ConversationViewItem) {
|
||||
guard let contact = viewItem.contactShare() else {
|
||||
guard let contactShare = viewItem.contactShare else {
|
||||
owsFail("\(logTag) missing contact.")
|
||||
return
|
||||
}
|
||||
let contactViewController = ContactViewController(contact: contact)
|
||||
let contactViewController = ContactViewController(contactShare: contactShare)
|
||||
self.navigationController?.pushViewController(contactViewController, animated: true)
|
||||
}
|
||||
|
||||
|
||||
132
SignalMessaging/ViewModels/ContactShareViewModel.swift
Normal file
132
SignalMessaging/ViewModels/ContactShareViewModel.swift
Normal file
@ -0,0 +1,132 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
public class ContactShareViewModel: NSObject {
|
||||
|
||||
public let dbRecord: OWSContact
|
||||
public let avatarImage: UIImage?
|
||||
|
||||
public required init(contactShareRecord: OWSContact, avatarImage: UIImage?) {
|
||||
self.dbRecord = contactShareRecord
|
||||
self.avatarImage = avatarImage
|
||||
}
|
||||
|
||||
public convenience init(contactShareRecord: OWSContact, transaction: YapDatabaseReadTransaction) {
|
||||
if let avatarAttachment = contactShareRecord.avatarAttachment(with: transaction) as? TSAttachmentStream {
|
||||
self.init(contactShareRecord: contactShareRecord, avatarImage: avatarAttachment.image())
|
||||
} else {
|
||||
self.init(contactShareRecord: contactShareRecord, avatarImage: nil)
|
||||
}
|
||||
}
|
||||
|
||||
public func getAvatarImage(diameter: CGFloat, contactsManager: OWSContactsManager) -> UIImage {
|
||||
if let avatarImage = avatarImage {
|
||||
return avatarImage
|
||||
}
|
||||
|
||||
// TODO: What's the best colorSeed value to use?
|
||||
let avatarBuilder = OWSContactAvatarBuilder(nonSignalName: displayName,
|
||||
colorSeed: displayName,
|
||||
diameter: UInt(diameter),
|
||||
contactsManager: contactsManager)
|
||||
return avatarBuilder.build()
|
||||
}
|
||||
|
||||
// MARK: Delegated -> dbRecord
|
||||
|
||||
public var addresses: [OWSContactAddress] {
|
||||
get {
|
||||
return dbRecord.addresses
|
||||
}
|
||||
set {
|
||||
return dbRecord.addresses = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public var emails: [OWSContactEmail] {
|
||||
get {
|
||||
return dbRecord.emails
|
||||
}
|
||||
set {
|
||||
dbRecord.emails = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public var phoneNumbers: [OWSContactPhoneNumber] {
|
||||
get {
|
||||
return dbRecord.phoneNumbers
|
||||
}
|
||||
set {
|
||||
dbRecord.phoneNumbers = newValue
|
||||
}
|
||||
}
|
||||
|
||||
public var phoneNumberStrings: [String] {
|
||||
return phoneNumbers.map { $0.phoneNumber }
|
||||
}
|
||||
|
||||
public var displayName: String {
|
||||
return dbRecord.displayName
|
||||
}
|
||||
|
||||
public var ows_isValid: Bool {
|
||||
return dbRecord.ows_isValid()
|
||||
}
|
||||
|
||||
public var namePrefix: String? {
|
||||
return dbRecord.namePrefix
|
||||
}
|
||||
|
||||
public var givenName: String? {
|
||||
return dbRecord.givenName
|
||||
}
|
||||
|
||||
public var middleName: String? {
|
||||
return dbRecord.middleName
|
||||
}
|
||||
|
||||
public var familyName: String? {
|
||||
return dbRecord.familyName
|
||||
}
|
||||
|
||||
public var nameSuffix: String? {
|
||||
return dbRecord.nameSuffix
|
||||
}
|
||||
|
||||
public var isProfileAvatar: Bool {
|
||||
return dbRecord.isProfileAvatar
|
||||
}
|
||||
|
||||
public func copy(withNamePrefix namePrefix: String?,
|
||||
givenName: String?,
|
||||
middleName: String?,
|
||||
familyName: String?,
|
||||
nameSuffix: String?) -> ContactShareViewModel {
|
||||
|
||||
// TODO move the `copy` logic into the view model?
|
||||
let newDbRecord = dbRecord.copy(withNamePrefix: namePrefix, givenName: givenName, middleName: middleName, familyName: familyName, nameSuffix: nameSuffix)
|
||||
|
||||
return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImage: self.avatarImage)
|
||||
}
|
||||
|
||||
public func newContact(withNamePrefix namePrefix: String?,
|
||||
givenName: String?,
|
||||
middleName: String?,
|
||||
familyName: String?,
|
||||
nameSuffix: String?) -> ContactShareViewModel {
|
||||
|
||||
// TODO move the `newContact` logic into the view model?
|
||||
let newDbRecord = dbRecord.newContact(withNamePrefix: namePrefix,
|
||||
givenName: givenName,
|
||||
middleName: middleName,
|
||||
familyName: familyName,
|
||||
nameSuffix: nameSuffix)
|
||||
|
||||
return ContactShareViewModel(contactShareRecord: newDbRecord, avatarImage: self.avatarImage)
|
||||
}
|
||||
|
||||
}
|
||||
@ -53,8 +53,9 @@
|
||||
}
|
||||
} else if (attachmentInfo.thumbnailAttachmentPointerId) {
|
||||
// download failed, or hasn't completed yet.
|
||||
TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentInfo.thumbnailAttachmentPointerId transaction:transaction];
|
||||
|
||||
TSAttachment *attachment =
|
||||
[TSAttachment fetchObjectWithUniqueID:attachmentInfo.thumbnailAttachmentPointerId transaction:transaction];
|
||||
|
||||
if ([attachment isKindOfClass:[TSAttachmentPointer class]]) {
|
||||
attachmentPointer = (TSAttachmentPointer *)attachment;
|
||||
if (attachmentPointer.state == TSAttachmentPointerStateFailed) {
|
||||
@ -7,8 +7,8 @@ import SignalServiceKit
|
||||
|
||||
@objc
|
||||
public protocol ApproveContactShareViewControllerDelegate: class {
|
||||
func approveContactShare(_ approveContactShare: ApproveContactShareViewController, didApproveContactShare contactShare: OWSContact)
|
||||
func approveContactShare(_ approveContactShare: ApproveContactShareViewController, didCancelContactShare contactShare: OWSContact)
|
||||
func approveContactShare(_ approveContactShare: ApproveContactShareViewController, didApproveContactShare contactShare: ContactShareViewModel)
|
||||
func approveContactShare(_ approveContactShare: ApproveContactShareViewController, didCancelContactShare contactShare: ContactShareViewModel)
|
||||
}
|
||||
|
||||
protocol ContactShareField: class {
|
||||
@ -19,7 +19,7 @@ protocol ContactShareField: class {
|
||||
|
||||
func setIsIncluded(_ isIncluded: Bool)
|
||||
|
||||
func applyToContact(contact: OWSContact)
|
||||
func applyToContact(contact: ContactShareViewModel)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -48,7 +48,7 @@ class ContactShareFieldBase<ContactFieldType: OWSContactField>: NSObject, Contac
|
||||
isIncludedFlag = isIncluded
|
||||
}
|
||||
|
||||
func applyToContact(contact: OWSContact) {
|
||||
func applyToContact(contact: ContactShareViewModel) {
|
||||
preconditionFailure("This method must be overridden")
|
||||
}
|
||||
}
|
||||
@ -57,7 +57,7 @@ class ContactShareFieldBase<ContactFieldType: OWSContactField>: NSObject, Contac
|
||||
|
||||
class ContactSharePhoneNumber: ContactShareFieldBase<OWSContactPhoneNumber> {
|
||||
|
||||
override func applyToContact(contact: OWSContact) {
|
||||
override func applyToContact(contact: ContactShareViewModel) {
|
||||
assert(isIncluded())
|
||||
|
||||
var values = [OWSContactPhoneNumber]()
|
||||
@ -71,7 +71,7 @@ class ContactSharePhoneNumber: ContactShareFieldBase<OWSContactPhoneNumber> {
|
||||
|
||||
class ContactShareEmail: ContactShareFieldBase<OWSContactEmail> {
|
||||
|
||||
override func applyToContact(contact: OWSContact) {
|
||||
override func applyToContact(contact: ContactShareViewModel) {
|
||||
assert(isIncluded())
|
||||
|
||||
var values = [OWSContactEmail]()
|
||||
@ -85,7 +85,7 @@ class ContactShareEmail: ContactShareFieldBase<OWSContactEmail> {
|
||||
|
||||
class ContactShareAddress: ContactShareFieldBase<OWSContactAddress> {
|
||||
|
||||
override func applyToContact(contact: OWSContact) {
|
||||
override func applyToContact(contact: ContactShareViewModel) {
|
||||
assert(isIncluded())
|
||||
|
||||
var values = [OWSContactAddress]()
|
||||
@ -185,7 +185,7 @@ public class ApproveContactShareViewController: OWSViewController, EditContactSh
|
||||
|
||||
let contactsManager: OWSContactsManager
|
||||
|
||||
var contactShare: OWSContact
|
||||
var contactShare: ContactShareViewModel
|
||||
|
||||
var fieldViews = [ContactShareFieldView]()
|
||||
|
||||
@ -199,7 +199,7 @@ public class ApproveContactShareViewController: OWSViewController, EditContactSh
|
||||
}
|
||||
|
||||
@objc
|
||||
required public init(contactShare: OWSContact, contactsManager: OWSContactsManager, delegate: ApproveContactShareViewControllerDelegate) {
|
||||
required public init(contactShare: ContactShareViewModel, contactsManager: OWSContactsManager, delegate: ApproveContactShareViewControllerDelegate) {
|
||||
self.contactsManager = contactsManager
|
||||
self.contactShare = contactShare
|
||||
self.delegate = delegate
|
||||
@ -277,7 +277,7 @@ public class ApproveContactShareViewController: OWSViewController, EditContactSh
|
||||
|
||||
// TODO: Surface error with resolution to user if not.
|
||||
func canShareContact() -> Bool {
|
||||
return contactShare.ows_isValid()
|
||||
return contactShare.ows_isValid
|
||||
}
|
||||
|
||||
func updateNavigationBar() {
|
||||
@ -471,7 +471,7 @@ public class ApproveContactShareViewController: OWSViewController, EditContactSh
|
||||
|
||||
// MARK: -
|
||||
|
||||
func filteredContactShare() -> OWSContact {
|
||||
func filteredContactShare() -> ContactShareViewModel {
|
||||
let result = self.contactShare.newContact(withNamePrefix: self.contactShare.namePrefix,
|
||||
givenName: self.contactShare.givenName,
|
||||
middleName: self.contactShare.middleName,
|
||||
@ -498,7 +498,7 @@ public class ApproveContactShareViewController: OWSViewController, EditContactSh
|
||||
}
|
||||
|
||||
let filteredContactShare = self.filteredContactShare()
|
||||
assert(filteredContactShare.ows_isValid())
|
||||
assert(filteredContactShare.ows_isValid)
|
||||
|
||||
delegate.approveContactShare(self, didApproveContactShare: filteredContactShare)
|
||||
}
|
||||
@ -523,7 +523,7 @@ public class ApproveContactShareViewController: OWSViewController, EditContactSh
|
||||
|
||||
// MARK: - EditContactShareNameViewControllerDelegate
|
||||
|
||||
public func editContactShareNameView(_ editContactShareNameView: EditContactShareNameViewController, didEditContactShare contactShare: OWSContact) {
|
||||
public func editContactShareNameView(_ editContactShareNameView: EditContactShareNameViewController, didEditContactShare contactShare: ContactShareViewModel) {
|
||||
self.contactShare = contactShare
|
||||
|
||||
nameLabel.text = contactShare.displayName
|
||||
|
||||
@ -109,7 +109,7 @@ class ContactNameFieldView: UIView {
|
||||
|
||||
@objc
|
||||
public protocol EditContactShareNameViewControllerDelegate: class {
|
||||
func editContactShareNameView(_ editContactShareNameView: EditContactShareNameViewController, didEditContactShare contactShare: OWSContact)
|
||||
func editContactShareNameView(_ editContactShareNameView: EditContactShareNameViewController, didEditContactShare contactShare: ContactShareViewModel)
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -118,7 +118,7 @@ public protocol EditContactShareNameViewControllerDelegate: class {
|
||||
public class EditContactShareNameViewController: OWSViewController, ContactNameFieldViewDelegate {
|
||||
weak var delegate: EditContactShareNameViewControllerDelegate?
|
||||
|
||||
let contactShare: OWSContact
|
||||
let contactShare: ContactShareViewModel
|
||||
|
||||
var namePrefixView: ContactNameFieldView!
|
||||
var givenNameView: ContactNameFieldView!
|
||||
@ -136,7 +136,7 @@ public class EditContactShareNameViewController: OWSViewController, ContactNameF
|
||||
}
|
||||
|
||||
@objc
|
||||
required public init(contactShare: OWSContact, delegate: EditContactShareNameViewControllerDelegate) {
|
||||
required public init(contactShare: ContactShareViewModel, delegate: EditContactShareNameViewControllerDelegate) {
|
||||
self.contactShare = contactShare
|
||||
self.delegate = delegate
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <SignalServiceKit/Contact.h>
|
||||
@ -77,6 +77,9 @@ extern NSString *const OWSContactsManagerSignalAccountsDidChangeNotification;
|
||||
- (nullable NSString *)nameFromSystemContactsForRecipientId:(NSString *)recipientId;
|
||||
- (NSString *)stringForConversationTitleWithPhoneIdentifier:(NSString *)recipientId;
|
||||
|
||||
- (nullable UIImage *)systemContactImageForPhoneIdentifier:(nullable NSString *)identifier;
|
||||
- (nullable UIImage *)profileImageForPhoneIdentifier:(nullable NSString *)identifier;
|
||||
|
||||
- (nullable UIImage *)imageForPhoneIdentifier:(nullable NSString *)identifier;
|
||||
- (NSAttributedString *)formattedDisplayNameForSignalAccount:(SignalAccount *)signalAccount font:(UIFont *_Nonnull)font;
|
||||
- (NSAttributedString *)formattedFullNameForRecipientId:(NSString *)recipientId font:(UIFont *)font;
|
||||
|
||||
@ -707,19 +707,32 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification
|
||||
return [self signalAccountForRecipientId:recipientId] != nil;
|
||||
}
|
||||
|
||||
- (UIImage *_Nullable)imageForPhoneIdentifier:(NSString *_Nullable)identifier
|
||||
|
||||
- (UIImage *_Nullable)systemContactImageForPhoneIdentifier:(NSString *_Nullable)identifier
|
||||
{
|
||||
Contact *contact = self.allContactsMap[identifier];
|
||||
if (!contact) {
|
||||
// If we haven't loaded system contacts yet, we may have a cached
|
||||
// copy in the db
|
||||
contact = [self signalAccountForRecipientId:identifier].contact;
|
||||
}
|
||||
|
||||
return contact.image;
|
||||
}
|
||||
|
||||
- (nullable UIImage *)profileImageForPhoneIdentifier:(nullable NSString *)identifier
|
||||
{
|
||||
return [self.profileManager profileAvatarForRecipientId:identifier];
|
||||
}
|
||||
|
||||
- (UIImage *_Nullable)imageForPhoneIdentifier:(NSString *_Nullable)identifier
|
||||
{
|
||||
// Prefer the contact image from the local address book if available
|
||||
UIImage *_Nullable image = contact.image;
|
||||
UIImage *_Nullable image = [self systemContactImageForPhoneIdentifier:identifier];
|
||||
|
||||
// Else try to use the image from their profile
|
||||
if (image == nil) {
|
||||
image = [self.profileManager profileAvatarForRecipientId:identifier];
|
||||
image = [self profileImageForPhoneIdentifier:identifier];
|
||||
}
|
||||
|
||||
return image;
|
||||
|
||||
@ -89,14 +89,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (nullable UIImage *)buildSavedImage
|
||||
{
|
||||
OWSRaiseException(
|
||||
NSInternalInconsistencyException, @"You must override %@ in a subclass", NSStringFromSelector(_cmd));
|
||||
OWS_ABSTRACT_METHOD();
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (UIImage *)buildDefaultImage
|
||||
{
|
||||
OWSRaiseException(
|
||||
NSInternalInconsistencyException, @"You must override %@ in a subclass", NSStringFromSelector(_cmd));
|
||||
OWS_ABSTRACT_METHOD();
|
||||
return [UIImage new];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -43,16 +43,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, readonly) NSArray<NSString *> *recipientIdentifiers;
|
||||
|
||||
#if TARGET_OS_IOS
|
||||
|
||||
/**
|
||||
* Returns the image representing the thread. Nil if not available.
|
||||
*
|
||||
* @return UIImage of the thread, or nil.
|
||||
*/
|
||||
- (nullable UIImage *)image;
|
||||
#endif
|
||||
|
||||
#pragma mark Interactions
|
||||
|
||||
/**
|
||||
|
||||
@ -112,11 +112,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return @[];
|
||||
}
|
||||
|
||||
- (nullable UIImage *)image
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)hasSafetyNumbers
|
||||
{
|
||||
return NO;
|
||||
@ -256,7 +251,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// who's test devices are constantly reinstalled. We could add a purpose-built DB view,
|
||||
// but I think in the real world this is rare to be a hotspot.
|
||||
if (missedCount > 50) {
|
||||
DDLogWarn(@"%@ found last interaction for inbox after skipping %tu items", self.logTag, missedCount);
|
||||
DDLogWarn(@"%@ found last interaction for inbox after skipping %lu items",
|
||||
self.logTag,
|
||||
(unsigned long)missedCount);
|
||||
}
|
||||
*stop = YES;
|
||||
}
|
||||
|
||||
@ -120,15 +120,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return [[TextSecureKitEnv sharedEnv].contactsManager displayNameForPhoneIdentifier:self.contactIdentifier];
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
- (nullable UIImage *)image
|
||||
{
|
||||
UIImage *image = [[TextSecureKitEnv sharedEnv].contactsManager imageForPhoneIdentifier:self.contactIdentifier];
|
||||
return image;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+ (NSString *)threadIdFromContactId:(NSString *)contactId {
|
||||
return [TSContactThreadPrefix stringByAppendingString:contactId];
|
||||
|
||||
@ -110,7 +110,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:attachmentPointer
|
||||
networkManager:self.networkManager];
|
||||
|
||||
DDLogDebug(@"%@ downloading thumbnail for transcript: %tu", self.logTag, transcript.timestamp);
|
||||
DDLogDebug(
|
||||
@"%@ downloading thumbnail for transcript: %lu", self.logTag, (unsigned long)transcript.timestamp);
|
||||
[attachmentProcessor fetchAttachmentsForMessage:outgoingMessage
|
||||
transaction:transaction
|
||||
success:^(TSAttachmentStream *_Nonnull attachmentStream) {
|
||||
@ -121,9 +122,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}];
|
||||
}
|
||||
failure:^(NSError *_Nonnull error) {
|
||||
DDLogWarn(@"%@ failed to fetch thumbnail for transcript: %tu with error: %@",
|
||||
DDLogWarn(@"%@ failed to fetch thumbnail for transcript: %lu with error: %@",
|
||||
self.logTag,
|
||||
transcript.timestamp,
|
||||
(unsigned long)transcript.timestamp,
|
||||
error);
|
||||
}];
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSSignalServiceProtosAttachmentPointer;
|
||||
@class TSAttachmentPointer;
|
||||
@class YapDatabaseReadWriteTransaction;
|
||||
|
||||
@ -79,6 +80,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (nullable TSAttachmentStream *)cloneAsThumbnail;
|
||||
|
||||
#pragma mark - Protobuf
|
||||
|
||||
+ (nullable OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentId:(nullable NSString *)filename
|
||||
isVoiceMessage:(BOOL)isVoiceMessage;
|
||||
|
||||
- (OWSSignalServiceProtosAttachmentPointer *)buildProtoWithFilename:(nullable NSString *)filename
|
||||
isVoiceMessage:(BOOL)isVoiceMessage;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -703,6 +703,76 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return thumbnailAttachment;
|
||||
}
|
||||
|
||||
// MARK: Protobuf serialization
|
||||
|
||||
+ (nullable OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentId:(nullable NSString *)attachmentId
|
||||
isVoiceMessage:(BOOL)isVoiceMessage
|
||||
{
|
||||
OWSAssert(attachmentId.length > 0);
|
||||
|
||||
// TODO we should past in a transaction, rather than sneakily generate one in `fetch...` to make sure we're
|
||||
// getting a consistent view in the message sending process. A brief glance shows it touches quite a bit of code,
|
||||
// but should be straight forward.
|
||||
TSAttachment *attachment = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
|
||||
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||
DDLogError(@"Unexpected type for attachment builder: %@", attachment);
|
||||
return nil;
|
||||
}
|
||||
|
||||
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
||||
return [attachmentStream buildProtoWithFilename:attachmentStream.sourceFilename isVoiceMessage:isVoiceMessage];
|
||||
}
|
||||
|
||||
// MJK can we get rid of the filename / isVoiceMessage pararms?
|
||||
// They seem to live (redundantly) on attachmentStream...
|
||||
+ (nullable OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentId:(NSString *)attachmentId
|
||||
filename:(nullable NSString *)filename
|
||||
isVoiceMessage:(BOOL)isVoiceMessage
|
||||
{
|
||||
OWSAssert(attachmentId.length > 0);
|
||||
|
||||
TSAttachment *attachment = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
|
||||
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||
DDLogError(@"Unexpected type for attachment builder: %@", attachment);
|
||||
return nil;
|
||||
}
|
||||
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
||||
return [attachmentStream buildProtoWithFilename:filename isVoiceMessage:isVoiceMessage];
|
||||
}
|
||||
|
||||
- (OWSSignalServiceProtosAttachmentPointer *)buildProtoWithFilename:(nullable NSString *)filename
|
||||
isVoiceMessage:(BOOL)isVoiceMessage
|
||||
{
|
||||
OWSSignalServiceProtosAttachmentPointerBuilder *builder = [OWSSignalServiceProtosAttachmentPointerBuilder new];
|
||||
|
||||
builder.id = self.serverId;
|
||||
|
||||
OWSAssert(self.contentType.length > 0);
|
||||
builder.contentType = self.contentType;
|
||||
|
||||
DDLogVerbose(@"%@ Sending attachment with filename: '%@'", self.logTag, filename);
|
||||
builder.fileName = filename;
|
||||
|
||||
builder.size = self.byteCount;
|
||||
builder.key = self.encryptionKey;
|
||||
builder.digest = self.digest;
|
||||
builder.flags = isVoiceMessage ? OWSSignalServiceProtosAttachmentPointerFlagsVoiceMessage : 0;
|
||||
|
||||
if (self.shouldHaveImageSize) {
|
||||
CGSize imageSize = self.imageSize;
|
||||
if (imageSize.width < NSIntegerMax && imageSize.height < NSIntegerMax) {
|
||||
NSInteger imageWidth = (NSInteger)round(imageSize.width);
|
||||
NSInteger imageHeight = (NSInteger)round(imageSize.height);
|
||||
if (imageWidth > 0 && imageHeight > 0) {
|
||||
builder.width = (UInt32)imageWidth;
|
||||
builder.height = (UInt32)imageHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [builder build];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@ -47,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
_quotedMessage =
|
||||
[TSQuotedMessage quotedMessageForDataMessage:_dataMessage thread:_thread relay:relay transaction:transaction];
|
||||
_contact = [OWSContacts contactForDataMessage:_dataMessage];
|
||||
_contact = [OWSContacts contactForDataMessage:_dataMessage relay:relay transaction:transaction];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -61,7 +61,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic) NSArray<OWSContactEmail *> *emails;
|
||||
@property (nonatomic) NSArray<OWSContactAddress *> *addresses;
|
||||
|
||||
@property (nonatomic, nullable) TSAttachment *avatar;
|
||||
@property (nonatomic) BOOL isProfileAvatar;
|
||||
|
||||
@end
|
||||
|
||||
@ -7,10 +7,13 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class CNContact;
|
||||
@class OWSAttachmentInfo;
|
||||
@class OWSSignalServiceProtosDataMessage;
|
||||
@class OWSSignalServiceProtosDataMessage;
|
||||
@class OWSSignalServiceProtosDataMessageContact;
|
||||
@class TSAttachment;
|
||||
@class TSAttachmentStream;
|
||||
@class YapDatabaseReadTransaction;
|
||||
@class YapDatabaseReadWriteTransaction;
|
||||
|
||||
extern BOOL kIsSendingContactSharesEnabled;
|
||||
@ -109,8 +112,9 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value);
|
||||
@property (nonatomic, readonly) NSArray<OWSContactEmail *> *emails;
|
||||
@property (nonatomic, readonly) NSArray<OWSContactAddress *> *addresses;
|
||||
|
||||
// TODO: This is provisional.
|
||||
@property (nonatomic, readonly, nullable) TSAttachment *avatar;
|
||||
@property (nonatomic, readonly, nullable) NSString *avatarAttachmentId;
|
||||
- (nullable TSAttachment *)avatarAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
- (void)saveAvatarImage:(UIImage *)image transaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
// "Profile" avatars should _not_ be saved to device contacts.
|
||||
@property (nonatomic, readonly) BOOL isProfileAvatar;
|
||||
|
||||
@ -160,7 +164,10 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value);
|
||||
#pragma mark - Proto Serialization
|
||||
|
||||
+ (nullable OWSSignalServiceProtosDataMessageContact *)protoForContact:(OWSContact *)contact;
|
||||
+ (OWSContact *_Nullable)contactForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage;
|
||||
|
||||
+ (nullable OWSContact *)contactForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
||||
relay:(nullable NSString *)relay
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -3,13 +3,14 @@
|
||||
//
|
||||
|
||||
#import "OWSContact.h"
|
||||
#import "MimeTypeUtil.h"
|
||||
#import "NSString+SSK.h"
|
||||
#import "OWSContact+Private.h"
|
||||
#import "OWSSignalServiceProtos.pb.h"
|
||||
#import "PhoneNumber.h"
|
||||
|
||||
//#import "Contact.h"
|
||||
#import "TSAttachment.h"
|
||||
#import "TSAttachmentPointer.h"
|
||||
#import "TSAttachmentStream.h"
|
||||
#import <YapDatabase/YapDatabaseTransaction.h>
|
||||
|
||||
@import Contacts;
|
||||
@ -273,7 +274,7 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
@property (nonatomic) NSArray<OWSContactEmail *> *emails;
|
||||
@property (nonatomic) NSArray<OWSContactAddress *> *addresses;
|
||||
|
||||
@property (nonatomic, nullable) TSAttachment *avatar;
|
||||
@property (nonatomic, nullable) NSString *avatarAttachmentId;
|
||||
@property (nonatomic) BOOL isProfileAvatar;
|
||||
|
||||
@end
|
||||
@ -456,6 +457,30 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
[self updateDisplayName];
|
||||
}
|
||||
|
||||
#pragma mark - Avatar
|
||||
|
||||
- (nullable TSAttachment *)avatarAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [TSAttachment fetchObjectWithUniqueID:self.avatarAttachmentId transaction:transaction];
|
||||
}
|
||||
|
||||
|
||||
- (void)saveAvatarImage:(UIImage *)image transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
NSData *imageData = UIImageJPEGRepresentation(image, (CGFloat)0.9);
|
||||
|
||||
TSAttachmentStream *attachmentStream = [[TSAttachmentStream alloc] initWithContentType:OWSMimeTypeImageJpeg
|
||||
byteCount:imageData.length
|
||||
sourceFilename:nil];
|
||||
|
||||
NSError *error;
|
||||
BOOL success = [attachmentStream writeData:imageData error:&error];
|
||||
OWSAssert(success && !error);
|
||||
|
||||
[attachmentStream saveWithTransaction:transaction];
|
||||
self.avatarAttachmentId = attachmentStream.uniqueId;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
@ -829,7 +854,13 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
[contactBuilder addAddress:addressBuilder.build];
|
||||
}
|
||||
|
||||
// TODO: avatar
|
||||
if (contact.avatarAttachmentId != nil) {
|
||||
OWSSignalServiceProtosDataMessageContactAvatarBuilder *avatarBuilder =
|
||||
[OWSSignalServiceProtosDataMessageContactAvatarBuilder new];
|
||||
avatarBuilder.avatar =
|
||||
[TSAttachmentStream buildProtoForAttachmentId:contact.avatarAttachmentId isVoiceMessage:NO];
|
||||
contactBuilder.avatar = [avatarBuilder build];
|
||||
}
|
||||
|
||||
OWSSignalServiceProtosDataMessageContact *contactProto = [contactBuilder build];
|
||||
if (contactProto.number.count < 1 && contactProto.email.count < 1 && contactProto.address.count < 1) {
|
||||
@ -839,7 +870,9 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
return contactProto;
|
||||
}
|
||||
|
||||
+ (OWSContact *_Nullable)contactForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
||||
+ (nullable OWSContact *)contactForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
|
||||
relay:(nullable NSString *)relay
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssert(dataMessage);
|
||||
|
||||
@ -905,10 +938,26 @@ NSString *NSStringForContactAddressType(OWSContactAddressType value)
|
||||
}
|
||||
contact.addresses = [addresses copy];
|
||||
|
||||
// TODO: Avatar
|
||||
|
||||
[contact ensureDisplayName];
|
||||
|
||||
if (contactProto.hasAvatar) {
|
||||
OWSSignalServiceProtosDataMessageContactAvatar *avatarInfo = contactProto.avatar;
|
||||
|
||||
if (avatarInfo.hasAvatar) {
|
||||
OWSSignalServiceProtosAttachmentPointer *avatarAttachment = avatarInfo.avatar;
|
||||
|
||||
TSAttachmentPointer *attachmentPointer =
|
||||
[TSAttachmentPointer attachmentPointerFromProto:avatarAttachment relay:relay];
|
||||
[attachmentPointer saveWithTransaction:transaction];
|
||||
|
||||
contact.avatarAttachmentId = attachmentPointer.uniqueId;
|
||||
contact.isProfileAvatar = avatarInfo.isProfile;
|
||||
} else {
|
||||
OWSFail(@"%@ in %s avatarInfo.hasAvatar was unexpectedly false", self.logTag, __PRETTY_FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return contact;
|
||||
}
|
||||
|
||||
|
||||
@ -129,8 +129,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString
|
||||
stringWithFormat:@"%@ in thread: %@ timestamp: %tu", [super description], self.uniqueThreadId, self.timestamp];
|
||||
return [NSString stringWithFormat:@"%@ in thread: %@ timestamp: %lu",
|
||||
[super description],
|
||||
self.uniqueThreadId,
|
||||
(unsigned long)self.timestamp];
|
||||
}
|
||||
|
||||
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction {
|
||||
|
||||
@ -42,6 +42,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (BOOL)hasAttachments;
|
||||
- (nullable TSAttachment *)attachmentWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
- (void)setQuotedMessageThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream;
|
||||
|
||||
- (BOOL)shouldStartExpireTimer;
|
||||
|
||||
@ -738,9 +738,9 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
|
||||
OWSFail(@"%@ message body length too long.", self.logTag);
|
||||
NSString *truncatedBody = [self.body copy];
|
||||
while ([truncatedBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > kOversizeTextMessageSizeThreshold) {
|
||||
DDLogError(@"%@ truncating body which is too long: %tu",
|
||||
DDLogError(@"%@ truncating body which is too long: %lu",
|
||||
self.logTag,
|
||||
[truncatedBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||
(unsigned long)[truncatedBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
|
||||
truncatedBody = [truncatedBody substringToIndex:truncatedBody.length / 2];
|
||||
}
|
||||
[builder setBody:truncatedBody];
|
||||
@ -799,6 +799,8 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
|
||||
[OWSContacts protoForContact:self.contactShare];
|
||||
if (contactProto) {
|
||||
[builder addContact:contactProto];
|
||||
} else {
|
||||
OWSFail(@"%@ in %s contactProto was unexpectedly nil", self.logTag, __PRETTY_FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
@ -834,7 +836,8 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
|
||||
quotedAttachmentBuilder.fileName = attachment.sourceFilename;
|
||||
if (attachment.thumbnailAttachmentStreamId) {
|
||||
quotedAttachmentBuilder.thumbnail =
|
||||
[self buildProtoForAttachmentId:attachment.thumbnailAttachmentStreamId];
|
||||
[TSAttachmentStream buildProtoForAttachmentId:attachment.thumbnailAttachmentStreamId
|
||||
isVoiceMessage:NO];
|
||||
}
|
||||
|
||||
[quoteBuilder addAttachments:[quotedAttachmentBuilder build]];
|
||||
@ -871,62 +874,6 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
|
||||
return !self.hasSyncedTranscript;
|
||||
}
|
||||
|
||||
- (OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentId:(NSString *)attachmentId
|
||||
{
|
||||
OWSAssert(attachmentId.length > 0);
|
||||
|
||||
TSAttachment *attachment = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
|
||||
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||
DDLogError(@"Unexpected type for attachment builder: %@", attachment);
|
||||
return nil;
|
||||
}
|
||||
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
||||
return [self buildProtoForAttachmentStream:attachmentStream filename:attachmentStream.sourceFilename];
|
||||
}
|
||||
|
||||
- (OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentId:(NSString *)attachmentId
|
||||
filename:(nullable NSString *)filename
|
||||
{
|
||||
OWSAssert(attachmentId.length > 0);
|
||||
|
||||
TSAttachment *attachment = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
|
||||
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||
DDLogError(@"Unexpected type for attachment builder: %@", attachment);
|
||||
return nil;
|
||||
}
|
||||
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
||||
return [self buildProtoForAttachmentStream:attachmentStream filename:filename];
|
||||
}
|
||||
|
||||
- (OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentStream:(TSAttachmentStream *)attachmentStream
|
||||
filename:(nullable NSString *)filename
|
||||
{
|
||||
OWSSignalServiceProtosAttachmentPointerBuilder *builder = [OWSSignalServiceProtosAttachmentPointerBuilder new];
|
||||
[builder setId:attachmentStream.serverId];
|
||||
OWSAssert(attachmentStream.contentType.length > 0);
|
||||
[builder setContentType:attachmentStream.contentType];
|
||||
DDLogVerbose(@"%@ Sending attachment with filename: '%@'", self.logTag, filename);
|
||||
[builder setFileName:filename];
|
||||
[builder setSize:attachmentStream.byteCount];
|
||||
[builder setKey:attachmentStream.encryptionKey];
|
||||
[builder setDigest:attachmentStream.digest];
|
||||
[builder setFlags:(self.isVoiceMessage ? OWSSignalServiceProtosAttachmentPointerFlagsVoiceMessage : 0)];
|
||||
|
||||
if ([attachmentStream shouldHaveImageSize]) {
|
||||
CGSize imageSize = [attachmentStream imageSize];
|
||||
if (imageSize.width < NSIntegerMax && imageSize.height < NSIntegerMax) {
|
||||
NSInteger imageWidth = (NSInteger)round(imageSize.width);
|
||||
NSInteger imageHeight = (NSInteger)round(imageSize.height);
|
||||
if (imageWidth > 0 && imageHeight > 0) {
|
||||
[builder setWidth:(UInt32)imageWidth];
|
||||
[builder setHeight:(UInt32)imageHeight];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [builder build];
|
||||
}
|
||||
|
||||
- (NSString *)statusDescription
|
||||
{
|
||||
NSMutableString *result = [NSMutableString new];
|
||||
|
||||
@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class TSThread;
|
||||
@class YapDatabaseReadWriteTransaction;
|
||||
|
||||
@interface OWSAttachmentInfo: MTLModel
|
||||
@interface OWSAttachmentInfo : MTLModel
|
||||
|
||||
@property (nonatomic, readonly, nullable) NSString *contentType;
|
||||
@property (nonatomic, readonly, nullable) NSString *sourceFilename;
|
||||
@ -38,7 +38,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
contentType:(NSString *)contentType
|
||||
sourceFilename:(NSString *)sourceFilename NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (instancetype)initWithAttachment:(TSAttachment *)attachment;
|
||||
- (instancetype)initWithAttachmentStream:(TSAttachmentStream *)attachmentStream;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@ -18,14 +18,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation OWSAttachmentInfo
|
||||
|
||||
- (instancetype)initWithAttachment:(TSAttachment *)attachment
|
||||
- (instancetype)initWithAttachmentStream:(TSAttachmentStream *)attachmentStream;
|
||||
{
|
||||
OWSAssert(attachment.uniqueId);
|
||||
OWSAssert(attachment.contentType);
|
||||
OWSAssert([attachmentStream isKindOfClass:[TSAttachmentStream class]]);
|
||||
OWSAssert(attachmentStream.uniqueId);
|
||||
OWSAssert(attachmentStream.contentType);
|
||||
|
||||
return [self initWithAttachmentId:attachment.uniqueId
|
||||
contentType:attachment.contentType
|
||||
sourceFilename:attachment.sourceFilename];
|
||||
return [self initWithAttachmentId:attachmentStream.uniqueId
|
||||
contentType:attachmentStream.contentType
|
||||
sourceFilename:attachmentStream.sourceFilename];
|
||||
}
|
||||
|
||||
- (instancetype)initWithAttachmentId:(nullable NSString *)attachmentId
|
||||
@ -94,8 +95,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
_body = body;
|
||||
|
||||
NSMutableArray *attachmentInfos = [NSMutableArray new];
|
||||
for (TSAttachment *attachment in attachments) {
|
||||
[attachmentInfos addObject:[[OWSAttachmentInfo alloc] initWithAttachment:attachment]];
|
||||
for (TSAttachmentStream *attachmentStream in attachments) {
|
||||
[attachmentInfos addObject:[[OWSAttachmentInfo alloc] initWithAttachmentStream:attachmentStream]];
|
||||
}
|
||||
_quotedAttachments = [attachmentInfos copy];
|
||||
|
||||
@ -152,19 +153,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
transaction:transaction];
|
||||
|
||||
if (thumbnailStream) {
|
||||
DDLogDebug(@"%@ Generated local thumbnail for quoted quoted message: %@:%tu",
|
||||
DDLogDebug(@"%@ Generated local thumbnail for quoted quoted message: %@:%lu",
|
||||
self.logTag,
|
||||
thread.uniqueId,
|
||||
timestamp);
|
||||
(unsigned long)timestamp);
|
||||
|
||||
[thumbnailStream saveWithTransaction:transaction];
|
||||
|
||||
attachmentInfo.thumbnailAttachmentStreamId = thumbnailStream.uniqueId;
|
||||
} else if (quotedAttachment.hasThumbnail) {
|
||||
DDLogDebug(@"%@ Saving reference for fetching remote thumbnail for quoted message: %@:%tu",
|
||||
DDLogDebug(@"%@ Saving reference for fetching remote thumbnail for quoted message: %@:%lu",
|
||||
self.logTag,
|
||||
thread.uniqueId,
|
||||
timestamp);
|
||||
(unsigned long)timestamp);
|
||||
|
||||
OWSSignalServiceProtosAttachmentPointer *thumbnailAttachmentProto = quotedAttachment.thumbnail;
|
||||
TSAttachmentPointer *thumbnailPointer =
|
||||
@ -173,7 +174,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
attachmentInfo.thumbnailAttachmentPointerId = thumbnailPointer.uniqueId;
|
||||
} else {
|
||||
DDLogDebug(@"%@ No thumbnail for quoted message: %@:%tu", self.logTag, thread.uniqueId, timestamp);
|
||||
DDLogDebug(
|
||||
@"%@ No thumbnail for quoted message: %@:%lu", self.logTag, thread.uniqueId, (unsigned long)timestamp);
|
||||
}
|
||||
|
||||
[attachmentInfos addObject:attachmentInfo];
|
||||
|
||||
@ -356,10 +356,10 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo
|
||||
|
||||
backgroundTask = nil;
|
||||
|
||||
DDLogVerbose(@"%@ completed %zu/%zu jobs. %zd jobs left.",
|
||||
DDLogVerbose(@"%@ completed %lu/%lu jobs. %zd jobs left.",
|
||||
self.logTag,
|
||||
processedJobs.count,
|
||||
batchJobs.count,
|
||||
(unsigned long)processedJobs.count,
|
||||
(unsigned long)batchJobs.count,
|
||||
[OWSMessageContentJob numberOfKeysInCollection]);
|
||||
|
||||
// Wait a bit in hopes of increasing the batch size.
|
||||
|
||||
@ -135,7 +135,7 @@ void AssertIsOnDisappearingMessagesQueue()
|
||||
transaction:transaction];
|
||||
}];
|
||||
|
||||
DDLogDebug(@"%@ Removed %tu expired messages", self.logTag, expirationCount);
|
||||
DDLogDebug(@"%@ Removed %lu expired messages", self.logTag, (unsigned long)expirationCount);
|
||||
|
||||
backgroundTask = nil;
|
||||
return expirationCount;
|
||||
@ -398,7 +398,7 @@ void AssertIsOnDisappearingMessagesQueue()
|
||||
{
|
||||
[self.disappearingMessagesFinder enumerateMessagesWhichFailedToStartExpiringWithBlock:^(
|
||||
TSMessage *_Nonnull message) {
|
||||
DDLogWarn(@"%@ starting old timer for message timestamp: %tu", self.logTag, message.timestamp);
|
||||
DDLogWarn(@"%@ starting old timer for message timestamp: %lu", self.logTag, (unsigned long)message.timestamp);
|
||||
[self setExpirationForMessage:message expirationStartedAt:message.timestampForSorting transaction:transaction];
|
||||
}
|
||||
transaction:transaction];
|
||||
|
||||
@ -909,7 +909,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
uint64_t timestamp = envelope.timestamp;
|
||||
NSString *body = dataMessage.body;
|
||||
NSData *groupId = dataMessage.hasGroup ? dataMessage.group.id : nil;
|
||||
OWSContact *_Nullable contact = [OWSContacts contactForDataMessage:dataMessage];
|
||||
OWSContact *_Nullable contact =
|
||||
[OWSContacts contactForDataMessage:dataMessage relay:envelope.relay transaction:transaction];
|
||||
|
||||
if (dataMessage.group.type == OWSSignalServiceProtosGroupContextTypeRequestInfo) {
|
||||
[self handleGroupInfoRequest:envelope dataMessage:dataMessage transaction:transaction];
|
||||
@ -1108,7 +1109,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:attachmentPointer
|
||||
networkManager:self.networkManager];
|
||||
|
||||
DDLogDebug(@"%@ downloading thumbnail for message: %tu", self.logTag, incomingMessage.timestamp);
|
||||
DDLogDebug(
|
||||
@"%@ downloading thumbnail for message: %lu", self.logTag, (unsigned long)incomingMessage.timestamp);
|
||||
[attachmentProcessor fetchAttachmentsForMessage:incomingMessage
|
||||
transaction:transaction
|
||||
success:^(TSAttachmentStream *_Nonnull attachmentStream) {
|
||||
@ -1119,14 +1121,45 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}];
|
||||
}
|
||||
failure:^(NSError *_Nonnull error) {
|
||||
DDLogWarn(@"%@ failed to fetch thumbnail for message: %tu with error: %@",
|
||||
DDLogWarn(@"%@ failed to fetch thumbnail for message: %lu with error: %@",
|
||||
self.logTag,
|
||||
incomingMessage.timestamp,
|
||||
(unsigned long)incomingMessage.timestamp,
|
||||
error);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
OWSContact *_Nullable contact = incomingMessage.contactShare;
|
||||
if (contact && contact.avatarAttachmentId) {
|
||||
TSAttachmentPointer *attachmentPointer =
|
||||
[TSAttachmentPointer fetchObjectWithUniqueID:contact.avatarAttachmentId transaction:transaction];
|
||||
|
||||
if (![attachmentPointer isKindOfClass:[TSAttachmentPointer class]]) {
|
||||
OWSFail(@"%@ in %s avatar attachmentPointer was unexpectedly nil", self.logTag, __PRETTY_FUNCTION__);
|
||||
} else {
|
||||
OWSAttachmentsProcessor *attachmentProcessor =
|
||||
[[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:attachmentPointer
|
||||
networkManager:self.networkManager];
|
||||
|
||||
DDLogDebug(@"%@ downloading contact avatar for message: %lu",
|
||||
self.logTag,
|
||||
(unsigned long)incomingMessage.timestamp);
|
||||
[attachmentProcessor fetchAttachmentsForMessage:incomingMessage
|
||||
transaction:transaction
|
||||
success:^(TSAttachmentStream *_Nonnull attachmentStream) {
|
||||
[self.dbConnection
|
||||
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
||||
[incomingMessage touchWithTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
failure:^(NSError *_Nonnull error) {
|
||||
DDLogWarn(@"%@ failed to fetch contact avatar for message: %lu with error: %@",
|
||||
self.logTag,
|
||||
(unsigned long)incomingMessage.timestamp,
|
||||
error);
|
||||
}];
|
||||
}
|
||||
}
|
||||
// In case we already have a read receipt for this new message (this happens sometimes).
|
||||
[OWSReadReceiptManager.sharedManager applyEarlyReadReceiptsForIncomingMessage:incomingMessage
|
||||
transaction:transaction];
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#import "NSError+MessageSending.h"
|
||||
#import "OWSBackgroundTask.h"
|
||||
#import "OWSBlockingManager.h"
|
||||
#import "OWSContact.h"
|
||||
#import "OWSDevice.h"
|
||||
#import "OWSDisappearingMessagesJob.h"
|
||||
#import "OWSError.h"
|
||||
@ -293,6 +294,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
__block NSArray<TSAttachmentStream *> *quotedThumbnailAttachments = @[];
|
||||
__block TSAttachmentStream *_Nullable contactShareAvatarAttachment;
|
||||
|
||||
// This method will use a read/write transaction. This transaction
|
||||
// will block until any open read/write transactions are complete.
|
||||
@ -312,6 +314,18 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
[message.quotedMessage createThumbnailAttachmentsIfNecessaryWithTransaction:transaction];
|
||||
}
|
||||
|
||||
if (message.contactShare.avatarAttachmentId != nil) {
|
||||
TSAttachment *avatarAttachment = [message.contactShare avatarAttachmentWithTransaction:transaction];
|
||||
if ([avatarAttachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||
contactShareAvatarAttachment = (TSAttachmentStream *)avatarAttachment;
|
||||
} else {
|
||||
OWSFail(@"%@ in %s unexpected avatarAttachment: %@",
|
||||
self.logTag,
|
||||
__PRETTY_FUNCTION__,
|
||||
avatarAttachment);
|
||||
}
|
||||
}
|
||||
|
||||
// All outgoing messages should be saved at the time they are enqueued.
|
||||
[message saveWithTransaction:transaction];
|
||||
// When we start a message send, all "failed" recipients should be marked as "sending".
|
||||
@ -325,6 +339,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
dbConnection:self.dbConnection
|
||||
success:successHandler
|
||||
failure:failureHandler];
|
||||
|
||||
// TODO de-dupe attachment enque logic.
|
||||
if (message.hasAttachments) {
|
||||
OWSUploadOperation *uploadAttachmentOperation =
|
||||
[[OWSUploadOperation alloc] initWithAttachmentId:message.attachmentIds.firstObject
|
||||
@ -349,6 +365,17 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|
||||
[sendingQueue addOperation:uploadQuoteThumbnailOperation];
|
||||
}
|
||||
|
||||
if (contactShareAvatarAttachment != nil) {
|
||||
OWSAssert(message.contactShare);
|
||||
OWSUploadOperation *uploadAvatarOperation =
|
||||
[[OWSUploadOperation alloc] initWithAttachmentId:contactShareAvatarAttachment.uniqueId
|
||||
dbConnection:self.dbConnection];
|
||||
|
||||
// TODO put attachment uploads on a (lowly) concurrent queue
|
||||
[sendMessageOperation addDependency:uploadAvatarOperation];
|
||||
[sendingQueue addOperation:uploadAvatarOperation];
|
||||
}
|
||||
|
||||
[sendingQueue addOperation:sendMessageOperation];
|
||||
});
|
||||
}
|
||||
|
||||
@ -559,9 +559,9 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
|
||||
}
|
||||
|
||||
if (wasLocal) {
|
||||
DDLogError(@"Marking %zu messages as read locally.", newlyReadList.count);
|
||||
DDLogError(@"Marking %lu messages as read locally.", (unsigned long)newlyReadList.count);
|
||||
} else {
|
||||
DDLogError(@"Marking %zu messages as read by linked device.", newlyReadList.count);
|
||||
DDLogError(@"Marking %lu messages as read by linked device.", (unsigned long)newlyReadList.count);
|
||||
}
|
||||
for (id<OWSReadTracking> readItem in newlyReadList) {
|
||||
[readItem markAsReadAtTimestamp:readTimestamp sendReadReceipt:wasLocal transaction:transaction];
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
@class Contact;
|
||||
@ -12,8 +12,4 @@
|
||||
- (NSString * _Nonnull)displayNameForPhoneIdentifier:(NSString * _Nullable)phoneNumber;
|
||||
- (NSArray<SignalAccount *> * _Nonnull)signalAccounts;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
- (UIImage * _Nullable)imageForPhoneIdentifier:(NSString * _Nullable)phoneNumber;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
#import "OWSOrphanedDataCleaner.h"
|
||||
#import "NSDate+OWS.h"
|
||||
#import "OWSContact.h"
|
||||
#import "OWSPrimaryStorage.h"
|
||||
#import "TSAttachmentStream.h"
|
||||
#import "TSInteraction.h"
|
||||
@ -82,18 +83,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}];
|
||||
}];
|
||||
|
||||
CleanupLogDebug(@"%@ fileCount: %zd", self.logTag, fileCount);
|
||||
CleanupLogDebug(@"%@ fileCount: %lu", self.logTag, (unsigned long)fileCount);
|
||||
CleanupLogDebug(@"%@ totalFileSize: %lld", self.logTag, totalFileSize);
|
||||
CleanupLogDebug(@"%@ attachmentStreams: %d", self.logTag, attachmentStreamCount);
|
||||
CleanupLogDebug(@"%@ attachmentStreams with file paths: %zd", self.logTag, attachmentFilePaths.count);
|
||||
CleanupLogDebug(
|
||||
@"%@ attachmentStreams with file paths: %lu", self.logTag, (unsigned long)attachmentFilePaths.count);
|
||||
|
||||
NSMutableSet<NSString *> *orphanDiskFilePaths = [diskFilePaths mutableCopy];
|
||||
[orphanDiskFilePaths minusSet:attachmentFilePaths];
|
||||
NSMutableSet<NSString *> *missingAttachmentFilePaths = [attachmentFilePaths mutableCopy];
|
||||
[missingAttachmentFilePaths minusSet:diskFilePaths];
|
||||
|
||||
CleanupLogDebug(@"%@ orphan disk file paths: %zd", self.logTag, orphanDiskFilePaths.count);
|
||||
CleanupLogDebug(@"%@ missing attachment file paths: %zd", self.logTag, missingAttachmentFilePaths.count);
|
||||
CleanupLogDebug(@"%@ orphan disk file paths: %lu", self.logTag, (unsigned long)orphanDiskFilePaths.count);
|
||||
CleanupLogDebug(
|
||||
@"%@ missing attachment file paths: %lu", self.logTag, (unsigned long)missingAttachmentFilePaths.count);
|
||||
|
||||
[self printPaths:orphanDiskFilePaths.allObjects label:@"orphan disk file paths"];
|
||||
[self printPaths:missingAttachmentFilePaths.allObjects label:@"missing attachment file paths"];
|
||||
@ -106,6 +109,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
NSMutableSet<NSString *> *orphanInteractionIds = [NSMutableSet new];
|
||||
NSMutableSet<NSString *> *messageAttachmentIds = [NSMutableSet new];
|
||||
NSMutableSet<NSString *> *quotedReplyThumbnailAttachmentIds = [NSMutableSet new];
|
||||
NSMutableSet<NSString *> *contactShareAvatarAttachmentIds = [NSMutableSet new];
|
||||
|
||||
[databaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[transaction enumerateKeysAndObjectsInCollection:TSMessage.collection
|
||||
@ -129,22 +133,33 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
addObjectsFromArray:quotedMessage
|
||||
.thumbnailAttachmentStreamIds];
|
||||
}
|
||||
|
||||
OWSContact *_Nullable contactShare = message.contactShare;
|
||||
if (contactShare && contactShare.avatarAttachmentId) {
|
||||
[contactShareAvatarAttachmentIds
|
||||
addObject:contactShare.avatarAttachmentId];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
CleanupLogDebug(@"%@ attachmentIds: %zd", self.logTag, attachmentIds.count);
|
||||
CleanupLogDebug(@"%@ messageAttachmentIds: %zd", self.logTag, messageAttachmentIds.count);
|
||||
CleanupLogDebug(@"%@ quotedReplyThumbnailAttachmentIds: %zd", self.logTag, quotedReplyThumbnailAttachmentIds.count);
|
||||
CleanupLogDebug(@"%@ attachmentIds: %lu", self.logTag, (unsigned long)attachmentIds.count);
|
||||
CleanupLogDebug(@"%@ messageAttachmentIds: %lu", self.logTag, (unsigned long)messageAttachmentIds.count);
|
||||
CleanupLogDebug(@"%@ quotedReplyThumbnailAttachmentIds: %lu",
|
||||
self.logTag,
|
||||
(unsigned long)quotedReplyThumbnailAttachmentIds.count);
|
||||
CleanupLogDebug(
|
||||
@"%@ contactShareAvatarAttachmentIds: %lu", self.logTag, (unsigned long)contactShareAvatarAttachmentIds.count);
|
||||
|
||||
NSMutableSet<NSString *> *orphanAttachmentIds = [attachmentIds mutableCopy];
|
||||
[orphanAttachmentIds minusSet:messageAttachmentIds];
|
||||
[orphanAttachmentIds minusSet:quotedReplyThumbnailAttachmentIds];
|
||||
[orphanAttachmentIds minusSet:contactShareAvatarAttachmentIds];
|
||||
NSMutableSet<NSString *> *missingAttachmentIds = [messageAttachmentIds mutableCopy];
|
||||
[missingAttachmentIds minusSet:attachmentIds];
|
||||
|
||||
CleanupLogDebug(@"%@ orphan attachmentIds: %zd", self.logTag, orphanAttachmentIds.count);
|
||||
CleanupLogDebug(@"%@ missing attachmentIds: %zd", self.logTag, missingAttachmentIds.count);
|
||||
CleanupLogDebug(@"%@ orphan interactions: %zd", self.logTag, orphanInteractionIds.count);
|
||||
CleanupLogDebug(@"%@ orphan attachmentIds: %lu", self.logTag, (unsigned long)orphanAttachmentIds.count);
|
||||
CleanupLogDebug(@"%@ missing attachmentIds: %lu", self.logTag, (unsigned long)missingAttachmentIds.count);
|
||||
CleanupLogDebug(@"%@ orphan interactions: %lu", self.logTag, (unsigned long)orphanInteractionIds.count);
|
||||
|
||||
// We need to avoid cleaning up new attachments and files that are still in the process of
|
||||
// being created/written, so we don't clean up anything recent.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user