From 33f6a9552058747d016a788f7de29144e598788e Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 14 Oct 2016 16:59:58 -0400 Subject: [PATCH] Explain send failures for text and media messages fixes #1231 Motivation ---------- Previously when messages failed to send, there was no reason given. Furthermore, when media messages failed to send there was no indication that any attempt to send the message even occurred, nor a retry dialog. UX Changes ---------- - Show "uploading" status for media - Show specific error message in retry-send dialog - Only scroll to bottom when new message is inserted - Show specific errors when group creation fails Code Changes ----------- - Updated incorrect references to TSMessageAdapters which were actually references to OWSMessageData - MessageSender was extracted from SSK MessagesManager - access MessagesManager as property - idiomatic init/properties for Env - log contact intersections - Move scroll-to-bottom animation to main thread. // FREEBIE --- Podfile | 2 +- Podfile.lock | 23 +- Signal/Signal-Info.plist | 4 +- Signal/src/AppDelegate.m | 15 +- Signal/src/Models/OWSCall.m | 5 + .../TSMessageAdapaters/OWSMessageData.h | 1 + .../TSMessageAdapaters/TSMessageAdapter.h | 3 + .../TSMessageAdapaters/TSMessageAdapter.m | 27 ++ .../TSVideoAttachmentAdapter.m | 2 +- Signal/src/contact/OWSContactsManager.m | 25 +- Signal/src/environment/Environment.h | 37 +-- Signal/src/environment/Environment.m | 77 +++--- Signal/src/environment/Release.m | 102 ++++---- Signal/src/network/PushManager.m | 61 +++-- .../view controllers/MessagesViewController.m | 236 +++++++++++------- .../view controllers/NewGroupViewController.m | 139 ++++++----- ...SConversationSettingsTableViewController.m | 30 ++- .../view controllers/SignalsViewController.m | 44 ++-- .../OWSOutgoingMessageCollectionViewCell.m | 1 + .../TSMessageAdapters/TSMessageAdapterTest.m | 83 +++--- Signal/translations/bin/auto-genstrings | 2 +- .../translations/en.lproj/Localizable.strings | Bin 57938 -> 58564 bytes 22 files changed, 562 insertions(+), 357 deletions(-) diff --git a/Podfile b/Podfile index 2847f8fe3..409a98217 100644 --- a/Podfile +++ b/Podfile @@ -3,7 +3,7 @@ source 'https://github.com/CocoaPods/Specs.git' target 'Signal' do pod 'SocketRocket', :git => 'https://github.com/facebook/SocketRocket.git' - pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git' + pod 'SignalServiceKit', git: 'https://github.com/WhisperSystems/SignalServiceKit.git', branch: 'mkirk/outgoing-media-status#1231' #pod 'SignalServiceKit', path: '../SignalServiceKit' pod 'OpenSSL' pod 'PastelogKit', '~> 1.3' diff --git a/Podfile.lock b/Podfile.lock index 68dfbe524..9f697ad4f 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -33,10 +33,10 @@ PODS: - JSQMessagesViewController (7.3.4): - JSQSystemSoundPlayer (~> 2.0.1) - JSQSystemSoundPlayer (2.0.1) - - libPhoneNumber-iOS (0.8.16) - - Mantle (2.0.7): - - Mantle/extobjc (= 2.0.7) - - Mantle/extobjc (2.0.7) + - libPhoneNumber-iOS (0.8.17) + - Mantle (2.1.0): + - Mantle/extobjc (= 2.1.0) + - Mantle/extobjc (2.1.0) - OpenSSL (1.0.210) - PastelogKit (1.3): - CocoaLumberjack (~> 2.0) @@ -44,7 +44,7 @@ PODS: - Reachability (3.2) - SAMKeychain (1.5.2) - SCWaveformView (1.0.0) - - SignalServiceKit (0.2.0): + - SignalServiceKit (0.3.0): - '25519' - AFNetworking - AxolotlKit @@ -122,19 +122,20 @@ DEPENDENCIES: - OpenSSL - PastelogKit (~> 1.3) - SCWaveformView (~> 1.0) - - SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`) + - SignalServiceKit (from `https://github.com/WhisperSystems/SignalServiceKit.git`, branch `mkirk/outgoing-media-status#1231`) - SocketRocket (from `https://github.com/facebook/SocketRocket.git`) - ZXingObjC EXTERNAL SOURCES: SignalServiceKit: + :branch: mkirk/outgoing-media-status#1231 :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :git: https://github.com/facebook/SocketRocket.git CHECKOUT OPTIONS: SignalServiceKit: - :commit: d4c55d69404c99927da716c443997415ad7bc6ba + :commit: 4ba1e86ec12c4e28de264fea59bd57af0aa3edea :git: https://github.com/WhisperSystems/SignalServiceKit.git SocketRocket: :commit: 41b57bb2fc292a814f758441a05243eb38457027 @@ -150,15 +151,15 @@ SPEC CHECKSUMS: HKDFKit: c058305d6f64b84f28c50bd7aa89574625bcb62a JSQMessagesViewController: 39fed975e3c9f8eba7292071e29eeb541d105e66 JSQSystemSoundPlayer: c5850e77a4363ffd374cd851154b9af93264ed8d - libPhoneNumber-iOS: acb5805f67892db37adc3440290a367923672b51 - Mantle: bc40bb061d8c2c6fb48d5083e04d928c3b7f73d9 + libPhoneNumber-iOS: 9f083847f8cb9b81064cff2ed2c98cbf18d9f9f2 + Mantle: 2fa750afa478cd625a94230fbf1c13462f29395b OpenSSL: 246ffb948e9d56466727fd318134af35f5aa764e PastelogKit: 7b475be4cf577713506a943dd940bcc0499c8bca ProtocolBuffers: d509225eb2ea43d9582a59e94348fcf86e2abd65 Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 SAMKeychain: 1865333198217411f35327e8da61b43de79b635b SCWaveformView: 52a96750255d817e300565a80c81fb643e233e07 - SignalServiceKit: 4e7a552635e10f4d94f0a047fc6554e932340b30 + SignalServiceKit: 8b115cfd63f9b814fa03fe61fd5d38ef9a548460 SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e SQLCipher: 4c768761421736a247ed6cf412d9045615d53dff TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c @@ -166,6 +167,6 @@ SPEC CHECKSUMS: YapDatabase: b1e43555a34a5298e23a045be96817a5ef0da58f ZXingObjC: bf15b3814f7a105b6d99f47da2333c93a063650a -PODFILE CHECKSUM: 93ccdbbb243044904d772ee53e00154890a9f82f +PODFILE CHECKSUM: 7dfde19734213e4ff876efa2ea10d536da2e0b47 COCOAPODS: 1.0.1 diff --git a/Signal/Signal-Info.plist b/Signal/Signal-Info.plist index 91be3a806..26d64a35d 100644 --- a/Signal/Signal-Info.plist +++ b/Signal/Signal-Info.plist @@ -21,7 +21,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.6.2 + 2.6.3 CFBundleSignature ???? CFBundleURLTypes @@ -38,7 +38,7 @@ CFBundleVersion - 2.6.2.0 + 2.6.3.4 ITSAppUsesNonExemptEncryption LOGS_EMAIL diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 5269b0938..19d677e16 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -6,6 +6,7 @@ #import "Environment.h" #import "NotificationsManager.h" #import "OWSContactsManager.h" +#import "OWSStaleNotificationObserver.h" #import "PreferencesUtil.h" #import "PushManager.h" #import "Release.h" @@ -15,9 +16,9 @@ #import "TSSocketManager.h" #import "TextSecureKitEnv.h" #import "VersionMigrations.h" -#import "OWSStaleNotificationObserver.h" #import #import +#import static NSString *const kStoryboardName = @"Storyboard"; static NSString *const kInitialViewControllerIdentifier = @"UserInitialViewController"; @@ -128,8 +129,16 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [TextSecureKitEnv sharedEnv].contactsManager = [Environment getCurrent].contactsManager; [[TSStorageManager sharedManager] setupDatabase]; [TextSecureKitEnv sharedEnv].notificationsManager = [[NotificationsManager alloc] init]; - self.incomingMessageReadObserver = [[OWSIncomingMessageReadObserver alloc] initWithStorageManager:[TSStorageManager sharedManager] - messagesManager:[TSMessagesManager sharedManager]]; + + OWSMessageSender *messageSender = + [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager + storageManager:[TSStorageManager sharedManager] + contactsManager:[Environment getCurrent].contactsManager + contactsUpdater:[Environment getCurrent].contactsUpdater]; + + self.incomingMessageReadObserver = + [[OWSIncomingMessageReadObserver alloc] initWithStorageManager:[TSStorageManager sharedManager] + messageSender:messageSender]; [self.incomingMessageReadObserver startObserving]; self.staleNotificationObserver = [OWSStaleNotificationObserver new]; diff --git a/Signal/src/Models/OWSCall.m b/Signal/src/Models/OWSCall.m index d390efed5..2b1145c20 100644 --- a/Signal/src/Models/OWSCall.m +++ b/Signal/src/Models/OWSCall.m @@ -178,6 +178,11 @@ NS_ASSUME_NONNULL_BEGIN return NO; } +- (BOOL)isOutgoingAndDelivered +{ + return NO; +} + - (NSUInteger)messageHash { return self.hash; diff --git a/Signal/src/Models/TSMessageAdapaters/OWSMessageData.h b/Signal/src/Models/TSMessageAdapaters/OWSMessageData.h index d48024b39..c3b6780d6 100644 --- a/Signal/src/Models/TSMessageAdapaters/OWSMessageData.h +++ b/Signal/src/Models/TSMessageAdapaters/OWSMessageData.h @@ -21,6 +21,7 @@ typedef NS_ENUM(NSInteger, TSMessageAdapterType) { @property (nonatomic, readonly) TSMessageAdapterType messageType; @property (nonatomic, readonly) TSInteraction *interaction; @property (nonatomic, readonly) BOOL isExpiringMessage; +@property (nonatomic, readonly) BOOL isOutgoingAndDelivered; @property (nonatomic, readonly) BOOL shouldStartExpireTimer; @property (nonatomic, readonly) uint64_t expiresAtSeconds; @property (nonatomic, readonly) uint32_t expiresInSeconds; diff --git a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h index 8058d4fb2..5bc1ba3f5 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h +++ b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.h @@ -23,6 +23,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) TSInteraction *interaction; @property (readonly) TSInfoMessageType infoMessageType; +@property (nonatomic, readonly) CGFloat mediaViewAlpha; +@property (nonatomic, readonly) BOOL isOutgoingAndDelivered; +@property (nonatomic, readonly) BOOL isMediaBeingSent; @end diff --git a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m index bbce77d1b..f92f0e0b3 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSMessageAdapter.m @@ -305,4 +305,31 @@ return self.outgoingMessageStatus; } +- (CGFloat)mediaViewAlpha +{ + return (CGFloat)(self.isMediaBeingSent ? 0.75 : 1); +} + +- (BOOL)isMediaBeingSent +{ + if ([self.interaction isKindOfClass:[TSOutgoingMessage class]]) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.interaction; + if (outgoingMessage.hasAttachments && outgoingMessage.messageState == TSOutgoingMessageStateAttemptingOut) { + return YES; + } + } + return NO; +} + +- (BOOL)isOutgoingAndDelivered +{ + if ([self.interaction isKindOfClass:[TSOutgoingMessage class]]) { + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.interaction; + if (outgoingMessage.messageState == TSOutgoingMessageStateDelivered) { + return YES; + } + } + return NO; +} + @end diff --git a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m index 927abe5c0..9ec46933e 100644 --- a/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m +++ b/Signal/src/Models/TSMessageAdapaters/TSVideoAttachmentAdapter.m @@ -225,7 +225,7 @@ _maskLayer.hidden = YES; _progressView.hidden = YES; _videoPlayButton.hidden = NO; - _attachment.isDownloaded = YES; + _attachment.isDownloaded = YES; // TODO isn't this redundant with attachment processor? [[TSMessagesManager sharedManager] .dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { [_attachment saveWithTransaction:transaction]; diff --git a/Signal/src/contact/OWSContactsManager.m b/Signal/src/contact/OWSContactsManager.m index cd8a46be8..6c60d037c 100644 --- a/Signal/src/contact/OWSContactsManager.m +++ b/Signal/src/contact/OWSContactsManager.m @@ -91,13 +91,16 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in - (void)intersectContacts { [[ContactsUpdater sharedUpdater] updateSignalContactIntersectionWithABContacts:self.allContacts success:^{ + DDLogInfo(@"%@ Successfully intersected contacts.", self.tag); } failure:^(NSError *error) { - [NSTimer scheduledTimerWithTimeInterval:60 - target:self - selector:@selector(intersectContacts) - userInfo:nil - repeats:NO]; + DDLogWarn(@"%@ Failed to intersect contacts with error: %@. Rescheduling", self.tag, error); + + [NSTimer scheduledTimerWithTimeInterval:60 + target:self + selector:@selector(intersectContacts) + userInfo:nil + repeats:NO]; }]; } @@ -423,4 +426,16 @@ void onAddressBookChanged(ABAddressBookRef notifyAddressBook, CFDictionaryRef in return nil; } +#pragma mark - Logging + ++ (NSString *)tag +{ + return [NSString stringWithFormat:@"[%@]", self.class]; +} + +- (NSString *)tag +{ + return self.class.tag; +} + @end diff --git a/Signal/src/environment/Environment.h b/Signal/src/environment/Environment.h index 5779f06b8..53f1ee236 100644 --- a/Signal/src/environment/Environment.h +++ b/Signal/src/environment/Environment.h @@ -29,8 +29,28 @@ static NSString *const kCallSegue = @"2.0_6.0_Call_Segue"; @class PhoneManager; @class SignalsViewController; @class TSGroupThread; +@class ContactsUpdater; +@class TSNetworkManager; @interface Environment : NSObject + +- (instancetype)initWithLogging:(id)logging + errorNoter:(ErrorHandlerBlock)errorNoter + serverPort:(in_port_t)serverPort + masterServerHostName:(NSString *)masterServerHostName + defaultRelayName:(NSString *)defaultRelayName + relayServerHostNameSuffix:(NSString *)relayServerHostNameSuffix + certificate:(Certificate *)certificate + supportedKeyAgreementProtocols:(NSArray *)keyAgreementProtocolsInDescendingPriority + phoneManager:(PhoneManager *)phoneManager + recentCallManager:(RecentCallManager *)recentCallManager + testingAndLegacyOptions:(NSArray *)testingAndLegacyOptions + zrtpClientId:(NSData *)zrtpClientId + zrtpVersionId:(NSData *)zrtpVersionId + contactsManager:(OWSContactsManager *)contactsManager + contactsUpdater:(ContactsUpdater *)contactsUpdater + networkManager:(TSNetworkManager *)networkManager; + @property (nonatomic, readonly) in_port_t serverPort; @property (nonatomic, readonly) id logging; @property (nonatomic, readonly) SecureEndPoint *masterServerSecureEndPoint; @@ -45,6 +65,8 @@ static NSString *const kCallSegue = @"2.0_6.0_Call_Segue"; @property (nonatomic, readonly) NSData *zrtpClientId; @property (nonatomic, readonly) NSData *zrtpVersionId; @property (nonatomic, readonly) OWSContactsManager *contactsManager; +@property (nonatomic, readonly) ContactsUpdater *contactsUpdater; +@property (nonatomic, readonly) TSNetworkManager *networkManager; @property (nonatomic, readonly) SignalsViewController *signalsViewController; @property (nonatomic, readonly, weak) UINavigationController *signUpFlowNavigationController; @@ -53,21 +75,6 @@ static NSString *const kCallSegue = @"2.0_6.0_Call_Segue"; + (SecureEndPoint *)getSecureEndPointToDefaultRelayServer; + (SecureEndPoint *)getSecureEndPointToSignalingServerNamed:(NSString *)name; -+ (Environment *)environmentWithLogging:(id)logging - andErrorNoter:(ErrorHandlerBlock)errorNoter - andServerPort:(in_port_t)serverPort - andMasterServerHostName:(NSString *)masterServerHostName - andDefaultRelayName:(NSString *)defaultRelayName - andRelayServerHostNameSuffix:(NSString *)relayServerHostNameSuffix - andCertificate:(Certificate *)certificate - andSupportedKeyAgreementProtocols:(NSArray *)keyAgreementProtocolsInDescendingPriority - andPhoneManager:(PhoneManager *)phoneManager - andRecentCallManager:(RecentCallManager *)recentCallManager - andTestingAndLegacyOptions:(NSArray *)testingAndLegacyOptions - andZrtpClientId:(NSData *)zrtpClientId - andZrtpVersionId:(NSData *)zrtpVersionId - andContactsManager:(OWSContactsManager *)contactsManager; - + (Environment *)getCurrent; + (void)setCurrent:(Environment *)curEnvironment; + (id)logging; diff --git a/Signal/src/environment/Environment.m b/Signal/src/environment/Environment.m index 01f1d00a9..e681d24c1 100644 --- a/Signal/src/environment/Environment.m +++ b/Signal/src/environment/Environment.m @@ -1,7 +1,7 @@ +#import "Environment.h" #import "Constraints.h" #import "DH3KKeyAgreementProtocol.h" #import "DebugLogger.h" -#import "Environment.h" #import "FunctionalUtil.h" #import "KeyAgreementProtocol.h" #import "MessagesViewController.h" @@ -10,6 +10,7 @@ #import "SignalsViewController.h" #import "TSContactThread.h" #import "TSGroupThread.h" +#import #define isRegisteredUserDefaultString @"isRegistered" @@ -17,10 +18,6 @@ static Environment *environment = nil; @implementation Environment -@synthesize testingAndLegacyOptions, errorNoter, keyAgreementProtocolsInDescendingPriority, logging, - masterServerSecureEndPoint, defaultRelayName, relayServerHostNameSuffix, certificate, serverPort, zrtpClientId, - zrtpVersionId, phoneManager, recentCallManager, contactsManager; - + (Environment *)getCurrent { NSAssert((environment != nil), @"Environment is not defined."); return environment; @@ -54,20 +51,23 @@ static Environment *environment = nil; return [SecureEndPoint secureEndPointForHost:location identifiedByCertificate:env.certificate]; } -+ (Environment *)environmentWithLogging:(id)logging - andErrorNoter:(ErrorHandlerBlock)errorNoter - andServerPort:(in_port_t)serverPort - andMasterServerHostName:(NSString *)masterServerHostName - andDefaultRelayName:(NSString *)defaultRelayName - andRelayServerHostNameSuffix:(NSString *)relayServerHostNameSuffix - andCertificate:(Certificate *)certificate - andSupportedKeyAgreementProtocols:(NSArray *)keyAgreementProtocolsInDescendingPriority - andPhoneManager:(PhoneManager *)phoneManager - andRecentCallManager:(RecentCallManager *)recentCallManager - andTestingAndLegacyOptions:(NSArray *)testingAndLegacyOptions - andZrtpClientId:(NSData *)zrtpClientId - andZrtpVersionId:(NSData *)zrtpVersionId - andContactsManager:(OWSContactsManager *)contactsManager { +- (instancetype)initWithLogging:(id)logging + errorNoter:(ErrorHandlerBlock)errorNoter + serverPort:(in_port_t)serverPort + masterServerHostName:(NSString *)masterServerHostName + defaultRelayName:(NSString *)defaultRelayName + relayServerHostNameSuffix:(NSString *)relayServerHostNameSuffix + certificate:(Certificate *)certificate + supportedKeyAgreementProtocols:(NSArray *)keyAgreementProtocolsInDescendingPriority + phoneManager:(PhoneManager *)phoneManager + recentCallManager:(RecentCallManager *)recentCallManager + testingAndLegacyOptions:(NSArray *)testingAndLegacyOptions + zrtpClientId:(NSData *)zrtpClientId + zrtpVersionId:(NSData *)zrtpVersionId + contactsManager:(OWSContactsManager *)contactsManager + contactsUpdater:(ContactsUpdater *)contactsUpdater + networkManager:(TSNetworkManager *)networkManager +{ ows_require(errorNoter != nil); ows_require(zrtpClientId != nil); ows_require(zrtpVersionId != nil); @@ -82,23 +82,29 @@ static Environment *environment = nil; return [p isKindOfClass:DH3KKeyAgreementProtocol.class]; }]); - Environment *e = [Environment new]; - e->errorNoter = errorNoter; - e->logging = logging; - e->testingAndLegacyOptions = testingAndLegacyOptions; - e->serverPort = serverPort; - e->masterServerSecureEndPoint = [SecureEndPoint + self = [super init]; + if (!self) { + return self; + } + + _errorNoter = errorNoter; + _logging = logging; + _testingAndLegacyOptions = testingAndLegacyOptions; + _serverPort = serverPort; + _masterServerSecureEndPoint = [SecureEndPoint secureEndPointForHost:[HostNameEndPoint hostNameEndPointWithHostName:masterServerHostName andPort:serverPort] identifiedByCertificate:certificate]; - e->defaultRelayName = defaultRelayName; - e->certificate = certificate; - e->relayServerHostNameSuffix = relayServerHostNameSuffix; - e->keyAgreementProtocolsInDescendingPriority = keyAgreementProtocolsInDescendingPriority; - e->phoneManager = phoneManager; - e->recentCallManager = recentCallManager; - e->zrtpClientId = zrtpClientId; - e->zrtpVersionId = zrtpVersionId; - e->contactsManager = contactsManager; + + _defaultRelayName = defaultRelayName; + _certificate = certificate; + _relayServerHostNameSuffix = relayServerHostNameSuffix; + _keyAgreementProtocolsInDescendingPriority = keyAgreementProtocolsInDescendingPriority; + _phoneManager = phoneManager; + _recentCallManager = recentCallManager; + _zrtpClientId = zrtpClientId; + _zrtpVersionId = zrtpVersionId; + _contactsManager = contactsManager; + _networkManager = networkManager; if (recentCallManager != nil) { // recentCallManagers are nil in unit tests because they would require unnecessary allocations. Detailed @@ -107,12 +113,13 @@ static Environment *environment = nil; [recentCallManager watchForCallsThrough:phoneManager untilCancelled:nil]; } - return e; + return self; } + (PhoneManager *)phoneManager { return Environment.getCurrent.phoneManager; } + + (id)logging { // Many tests create objects that rely on Environment only for logging. // So we bypass the nil check in getCurrent and silently don't log during unit testing, instead of failing hard. diff --git a/Signal/src/environment/Release.m b/Signal/src/environment/Release.m index 45ccb9c28..e43cc5df7 100644 --- a/Signal/src/environment/Release.m +++ b/Signal/src/environment/Release.m @@ -1,8 +1,10 @@ +#import "Release.h" #import "DiscardingLog.h" #import "PhoneManager.h" #import "PhoneNumberUtil.h" #import "RecentCallManager.h" -#import "Release.h" +#import +#import #define RELEASE_ZRTP_CLIENT_ID @"Whisper 000 ".encodedAsAscii #define RELEASE_ZRTP_VERSION_ID @"1.10".encodedAsAscii @@ -42,23 +44,22 @@ static unsigned char DH3K_PRIME[] = { DDLogError(@"%@: %@, %d", error, relatedInfo, causedTermination); }; - return [Environment - environmentWithLogging:logging - andErrorNoter:errorNoter - andServerPort:31337 - andMasterServerHostName:@"master.whispersystems.org" - andDefaultRelayName:@"relay" - andRelayServerHostNameSuffix:@"whispersystems.org" - andCertificate:[Certificate certificateFromResourcePath:@"redphone" ofType:@"cer"] - andSupportedKeyAgreementProtocols:[self supportedKeyAgreementProtocols] - andPhoneManager:[PhoneManager phoneManagerWithErrorHandler:errorNoter] - andRecentCallManager:[RecentCallManager new] - andTestingAndLegacyOptions:@[ - ENVIRONMENT_LEGACY_OPTION_RTP_PADDING_BIT_IMPLIES_EXTENSION_BIT_AND_TWELVE_EXTRA_ZERO_BYTES_IN_HEADER - ] - andZrtpClientId:RELEASE_ZRTP_CLIENT_ID - andZrtpVersionId:RELEASE_ZRTP_VERSION_ID - andContactsManager:[OWSContactsManager new]]; + return [[Environment alloc] initWithLogging:logging + errorNoter:errorNoter + serverPort:31337 + masterServerHostName:@"master.whispersystems.org" + defaultRelayName:@"relay" + relayServerHostNameSuffix:@"whispersystems.org" + certificate:[Certificate certificateFromResourcePath:@"redphone" ofType:@"cer"] + supportedKeyAgreementProtocols:[self supportedKeyAgreementProtocols] + phoneManager:[PhoneManager phoneManagerWithErrorHandler:errorNoter] + recentCallManager:[RecentCallManager new] + testingAndLegacyOptions:@[ ENVIRONMENT_LEGACY_OPTION_RTP_PADDING_BIT_IMPLIES_EXTENSION_BIT_AND_TWELVE_EXTRA_ZERO_BYTES_IN_HEADER ] + zrtpClientId:RELEASE_ZRTP_CLIENT_ID + zrtpVersionId:RELEASE_ZRTP_VERSION_ID + contactsManager:[OWSContactsManager new] + contactsUpdater:[ContactsUpdater sharedUpdater] + networkManager:[TSNetworkManager sharedManager]]; } + (Environment *)stagingEnvironmentWithLogging:(id)logging { @@ -66,23 +67,22 @@ static unsigned char DH3K_PRIME[] = { DDLogError(@"%@: %@, %d", error, relatedInfo, causedTermination); }; - return [Environment - environmentWithLogging:logging - andErrorNoter:errorNoter - andServerPort:31337 - andMasterServerHostName:@"redphone-staging.whispersystems.org" - andDefaultRelayName:@"redphone-staging-relay" - andRelayServerHostNameSuffix:@"whispersystems.org" - andCertificate:[Certificate certificateFromResourcePath:@"redphone" ofType:@"cer"] - andSupportedKeyAgreementProtocols:[self supportedKeyAgreementProtocols] - andPhoneManager:[PhoneManager phoneManagerWithErrorHandler:errorNoter] - andRecentCallManager:[RecentCallManager new] - andTestingAndLegacyOptions:@[ - ENVIRONMENT_LEGACY_OPTION_RTP_PADDING_BIT_IMPLIES_EXTENSION_BIT_AND_TWELVE_EXTRA_ZERO_BYTES_IN_HEADER - ] - andZrtpClientId:RELEASE_ZRTP_CLIENT_ID - andZrtpVersionId:RELEASE_ZRTP_VERSION_ID - andContactsManager:[OWSContactsManager new]]; + return [[Environment alloc] initWithLogging:logging + errorNoter:errorNoter + serverPort:31337 + masterServerHostName:@"redphone-staging.whispersystems.org" + defaultRelayName:@"redphone-staging-relay" + relayServerHostNameSuffix:@"whispersystems.org" + certificate:[Certificate certificateFromResourcePath:@"redphone" ofType:@"cer"] + supportedKeyAgreementProtocols:[self supportedKeyAgreementProtocols] + phoneManager:[PhoneManager phoneManagerWithErrorHandler:errorNoter] + recentCallManager:[RecentCallManager new] + testingAndLegacyOptions:@[ ENVIRONMENT_LEGACY_OPTION_RTP_PADDING_BIT_IMPLIES_EXTENSION_BIT_AND_TWELVE_EXTRA_ZERO_BYTES_IN_HEADER ] + zrtpClientId:RELEASE_ZRTP_CLIENT_ID + zrtpVersionId:RELEASE_ZRTP_VERSION_ID + contactsManager:[OWSContactsManager new] + contactsUpdater:[ContactsUpdater sharedUpdater] + networkManager:[TSNetworkManager sharedManager]]; } + (Environment *)unitTestEnvironment:(NSArray *)testingAndLegacyOptions { @@ -91,21 +91,23 @@ static unsigned char DH3K_PRIME[] = { keyAgreementProtocols = @[ [Release supportedDH3KKeyAgreementProtocol] ]; } - return [Environment environmentWithLogging:[DiscardingLog discardingLog] - andErrorNoter:^(id error, id relatedInfo, bool causedTermination) { - } - andServerPort:31337 - andMasterServerHostName:@"master.whispersystems.org" - andDefaultRelayName:@"relay" - andRelayServerHostNameSuffix:@"whispersystems.org" - andCertificate:[Certificate certificateFromResourcePath:@"redphone" ofType:@"cer"] - andSupportedKeyAgreementProtocols:keyAgreementProtocols - andPhoneManager:nil - andRecentCallManager:nil - andTestingAndLegacyOptions:testingAndLegacyOptions - andZrtpClientId:TESTING_ZRTP_CLIENT_ID - andZrtpVersionId:TESTING_ZRTP_VERSION_ID - andContactsManager:nil]; + return [[Environment alloc] initWithLogging:[DiscardingLog discardingLog] + errorNoter:^(id error, id relatedInfo, bool causedTermination) { + } + serverPort:31337 + masterServerHostName:@"master.whispersystems.org" + defaultRelayName:@"relay" + relayServerHostNameSuffix:@"whispersystems.org" + certificate:[Certificate certificateFromResourcePath:@"redphone" ofType:@"cer"] + supportedKeyAgreementProtocols:keyAgreementProtocols + phoneManager:nil + recentCallManager:nil + testingAndLegacyOptions:testingAndLegacyOptions + zrtpClientId:TESTING_ZRTP_CLIENT_ID + zrtpVersionId:TESTING_ZRTP_VERSION_ID + contactsManager:nil + contactsUpdater:[ContactsUpdater sharedUpdater] + networkManager:[TSNetworkManager sharedManager]]; } + (NSArray *)supportedKeyAgreementProtocols { diff --git a/Signal/src/network/PushManager.m b/Signal/src/network/PushManager.m index e2b3e6d12..1571d46b8 100644 --- a/Signal/src/network/PushManager.m +++ b/Signal/src/network/PushManager.m @@ -6,17 +6,18 @@ // Copyright (c) 2014 Open Whisper Systems. All rights reserved. // +#import "PushManager.h" #import "AppDelegate.h" -#import "OWSContactsManager.h" #import "InCallViewController.h" #import "NSData+ows_StripToken.h" #import "NSDate+millisecondTimeStamp.h" #import "NotificationTracker.h" +#import "OWSContactsManager.h" #import "PreferencesUtil.h" -#import "PushManager.h" #import "RPServerRequestsManager.h" -#import "TSMessagesManager+sendMessages.h" +#import "TSOutgoingMessage.h" #import "TSSocketManager.h" +#import #define pushManagerDomain @"org.whispersystems.pushmanager" @@ -29,6 +30,7 @@ @property (nonatomic, retain) NSMutableArray *currentNotifications; @property (nonatomic) UIBackgroundTaskIdentifier callBackgroundTask; @property (nonatomic, readonly) OWSContactsManager *contactsManager; +@property (nonatomic, readonly) OWSMessageSender *messageSender; @end @@ -38,19 +40,38 @@ static PushManager *sharedManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedManager = [self new]; + sharedManager = [[self alloc] initDefault]; }); return sharedManager; } -- (instancetype)init { +- (instancetype)initDefault +{ + return [self initWithContactsManager:[Environment getCurrent].contactsManager + notificationTracker:[NotificationTracker notificationTracker] + networkManager:[Environment getCurrent].networkManager + storageManager:[TSStorageManager sharedManager] + contactsUpdater:[Environment getCurrent].contactsUpdater]; +} + +- (instancetype)initWithContactsManager:(OWSContactsManager *)contactsManager + notificationTracker:(NotificationTracker *)notificationTracker + networkManager:(TSNetworkManager *)networkManager + storageManager:(TSStorageManager *)storageManager + contactsUpdater:(ContactsUpdater *)contactsUpdater +{ self = [super init]; if (!self) { return self; } - _contactsManager = [Environment getCurrent].contactsManager; - _notificationTracker = [NotificationTracker notificationTracker]; + _contactsManager = contactsManager; + _notificationTracker = notificationTracker; + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:networkManager + storageManager:storageManager + contactsManager:contactsManager + contactsUpdater:contactsUpdater]; + _missingPermissionsAlertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ACTION_REQUIRED_TITLE", @"") message:NSLocalizedString(@"PUSH_SETTINGS_MESSAGE", @"") delegate:nil @@ -107,7 +128,7 @@ notification.category = Signal_Call_Category; notification.soundName = @"r.caf"; - [[PushManager sharedManager] presentNotification:notification]; + [self presentNotification:notification]; _lastCallNotification = notification; if (_callBackgroundTask == UIBackgroundTaskInvalid) { @@ -185,19 +206,21 @@ [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:responseInfo[UIUserNotificationActionResponseTypedTextKey]]; - [[TSMessagesManager sharedManager] sendMessage:message - inThread:thread + [self.messageSender sendMessage:message success:^{ - [self markAllInThreadAsRead:notification.userInfo completionHandler:completionHandler]; - [[[[Environment getCurrent] signalsViewController] tableView] reloadData]; + [self markAllInThreadAsRead:notification.userInfo completionHandler:completionHandler]; + [[[[Environment getCurrent] signalsViewController] tableView] reloadData]; } - failure:^{ - UILocalNotification *failedSendNotif = [[UILocalNotification alloc] init]; - failedSendNotif.alertBody = - [NSString stringWithFormat:NSLocalizedString(@"NOTIFICATION_SEND_FAILED", nil), [thread name]]; - failedSendNotif.userInfo = @{Signal_Thread_UserInfo_Key : thread.uniqueId}; - [[PushManager sharedManager] presentNotification:failedSendNotif]; - completionHandler(); + failure:^(NSError *error) { + // TODO Surface the specific error in the notification? + DDLogError(@"Message send failed with error: %@", error); + + UILocalNotification *failedSendNotif = [[UILocalNotification alloc] init]; + failedSendNotif.alertBody = + [NSString stringWithFormat:NSLocalizedString(@"NOTIFICATION_SEND_FAILED", nil), [thread name]]; + failedSendNotif.userInfo = @{ Signal_Thread_UserInfo_Key : thread.uniqueId }; + [self presentNotification:failedSendNotif]; + completionHandler(); }]; } } else if ([identifier isEqualToString:Signal_Call_Accept_Identifier]) { diff --git a/Signal/src/view controllers/MessagesViewController.m b/Signal/src/view controllers/MessagesViewController.m index 5869300e1..56fd28c73 100644 --- a/Signal/src/view controllers/MessagesViewController.m +++ b/Signal/src/view controllers/MessagesViewController.m @@ -38,8 +38,6 @@ #import "TSIncomingMessage.h" #import "TSInfoMessage.h" #import "TSInvalidIdentityKeyErrorMessage.h" -#import "TSMessagesManager+attachments.h" -#import "TSMessagesManager+sendMessages.h" #import "UIFont+OWS.h" #import "UIUtil.h" #import @@ -53,11 +51,16 @@ #import #import #import +#import #import #import #import +#import #import #import +#import +#import +#import #import @import Photos; @@ -116,7 +119,11 @@ typedef enum : NSUInteger { @property (nonatomic, readonly) TSStorageManager *storageManager; @property (nonatomic, readonly) OWSContactsManager *contactsManager; +@property (nonatomic, readonly) ContactsUpdater *contactsUpdater; @property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob; +@property (nonatomic, readonly) TSMessagesManager *messagesManager; +@property (nonatomic, readonly) TSNetworkManager *networkManager; +@property (nonatomic, readonly) OWSMessageSender *messageSender; @property NSCache *messageAdapterCache; @@ -136,9 +143,16 @@ typedef enum : NSUInteger { return self; } - _contactsManager = [[Environment getCurrent] contactsManager]; + _contactsManager = [Environment getCurrent].contactsManager; + _contactsUpdater = [Environment getCurrent].contactsUpdater; _storageManager = [TSStorageManager sharedManager]; _disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:_storageManager]; + _messagesManager = [TSMessagesManager sharedManager]; + _networkManager = [TSNetworkManager sharedManager]; + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:_networkManager + storageManager:_storageManager + contactsManager:_contactsManager + contactsUpdater:_contactsUpdater]; return self; } @@ -150,9 +164,16 @@ typedef enum : NSUInteger { return self; } - _contactsManager = [[Environment getCurrent] contactsManager]; + _contactsManager = [Environment getCurrent].contactsManager; + _contactsUpdater = [Environment getCurrent].contactsUpdater; _storageManager = [TSStorageManager sharedManager]; _disappearingMessagesJob = [[OWSDisappearingMessagesJob alloc] initWithStorageManager:_storageManager]; + _messagesManager = [TSMessagesManager sharedManager]; + _networkManager = [TSNetworkManager sharedManager]; + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:_networkManager + storageManager:_storageManager + contactsManager:_contactsManager + contactsUpdater:_contactsUpdater]; return self; } @@ -397,12 +418,12 @@ typedef enum : NSUInteger { - (void)updateBackButtonAsync { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - NSUInteger count = [[TSMessagesManager sharedManager] unreadMessagesCountExcept:self.thread]; - dispatch_async(dispatch_get_main_queue(), ^{ - if (self) { - [self setUnreadCount:count]; - } - }); + NSUInteger count = [self.messagesManager unreadMessagesCountExcept:self.thread]; + dispatch_async(dispatch_get_main_queue(), ^{ + if (self) { + [self setUnreadCount:count]; + } + }); }); } @@ -650,13 +671,12 @@ typedef enum : NSUInteger { messageBody:text]; } - [[TSMessagesManager sharedManager] sendMessage:message - inThread:self.thread + [self.messageSender sendMessage:message success:^{ DDLogInfo(@"%@ Successfully sent message.", self.tag); } - failure:^{ - DDLogWarn(@"%@ Failed to deliver message.", self.tag); + failure:^(NSError *error) { + DDLogWarn(@"%@ Failed to deliver message with error: %@", self.tag, error); }]; [self finishSendingMessage]; } @@ -797,7 +817,13 @@ typedef enum : NSUInteger { [self fixupiOS10EmojiBugForTextView:cell.textView]; // END HACK iOS10EmojiBug see: https://github.com/WhisperSystems/Signal-iOS/issues/1368 - if (!message.isMediaMessage) { + if (message.isMediaMessage) { + if (![message isKindOfClass:[TSMessageAdapter class]]) { + DDLogError(@"%@ Unexpected media message:%@", self.tag, message.class); + } + TSMessageAdapter *messageAdapter = (TSMessageAdapter *)message; + cell.mediaView.alpha = messageAdapter.mediaViewAlpha; + } else { cell.textView.textColor = [UIColor whiteColor]; cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor, @@ -937,9 +963,9 @@ typedef enum : NSUInteger { if (indexPath.row == 0) { showDate = YES; } else { - TSMessageAdapter *currentMessage = [self messageAtIndexPath:indexPath]; + id currentMessage = [self messageAtIndexPath:indexPath]; - TSMessageAdapter *previousMessage = + id previousMessage = [self messageAtIndexPath:[NSIndexPath indexPathForItem:indexPath.row - 1 inSection:indexPath.section]]; NSTimeInterval timeDifference = [currentMessage.date timeIntervalSinceDate:previousMessage.date]; @@ -953,7 +979,7 @@ typedef enum : NSUInteger { - (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath { if ([self showDateAtIndexPath:indexPath]) { - TSMessageAdapter *currentMessage = [self messageAtIndexPath:indexPath]; + id currentMessage = [self messageAtIndexPath:indexPath]; return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:currentMessage.date]; } @@ -963,7 +989,7 @@ typedef enum : NSUInteger { - (BOOL)shouldShowMessageStatusAtIndexPath:(NSIndexPath *)indexPath { - TSMessageAdapter *currentMessage = [self messageAtIndexPath:indexPath]; + id currentMessage = [self messageAtIndexPath:indexPath]; if (currentMessage.isExpiringMessage) { return YES; @@ -972,13 +998,14 @@ typedef enum : NSUInteger { return !![self collectionView:self.collectionView attributedTextForCellBottomLabelAtIndexPath:indexPath]; } -- (TSMessageAdapter *)nextOutgoingMessage:(NSIndexPath *)indexPath { - TSMessageAdapter *nextMessage = +- (id)nextOutgoingMessage:(NSIndexPath *)indexPath +{ + id nextMessage = [self messageAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row + 1 inSection:indexPath.section]]; int i = 1; - while (indexPath.item + i < [self.collectionView numberOfItemsInSection:indexPath.section] - 1 && - ![self isMessageOutgoingAndDelivered:nextMessage]) { + while (indexPath.item + i < [self.collectionView numberOfItemsInSection:indexPath.section] - 1 + && !nextMessage.isOutgoingAndDelivered) { i++; nextMessage = [self messageAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row + i inSection:indexPath.section]]; @@ -987,18 +1014,6 @@ typedef enum : NSUInteger { return nextMessage; } -- (BOOL)isMessageOutgoingAndDelivered:(TSMessageAdapter *)message -{ - if (message.messageType == TSOutgoingMessageAdapter) { - TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message; - if(outgoingMessage.messageState == TSOutgoingMessageStateDelivered) { - return YES; - } - } - return NO; -} - - - (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellBottomLabelAtIndexPath:(NSIndexPath *)indexPath { @@ -1011,11 +1026,8 @@ typedef enum : NSUInteger { if (message.messageType == TSOutgoingMessageAdapter) { TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)message.interaction; if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { - NSAttributedString *failedString = - [[NSAttributedString alloc] initWithString:NSLocalizedString(@"FAILED_SENDING_TEXT", nil)]; - - return failedString; - } else if ([self isMessageOutgoingAndDelivered:message]) { + return [[NSAttributedString alloc] initWithString:NSLocalizedString(@"FAILED_SENDING_TEXT", nil)]; + } else if (message.isOutgoingAndDelivered) { NSAttributedString *deliveredString = [[NSAttributedString alloc] initWithString:NSLocalizedString(@"DELIVERED_MESSAGE_TEXT", @"")]; @@ -1027,10 +1039,13 @@ typedef enum : NSUInteger { // Or when the next message is *not* an outgoing delivered message. TSMessageAdapter *nextMessage = [self nextOutgoingMessage:indexPath]; - if (![self isMessageOutgoingAndDelivered:nextMessage]) { + if (!nextMessage.isOutgoingAndDelivered) { [self updateLastDeliveredMessage:message]; return deliveredString; } + } else if (message.isMediaBeingSent) { + return [[NSAttributedString alloc] initWithString:NSLocalizedString(@"UPLOADING_MESSAGE_TEXT", + @"message footer while attachment is uploading")]; } } else if (message.messageType == TSIncomingMessageAdapter && [self.thread isKindOfClass:[TSGroupThread class]]) { TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message.interaction; @@ -1101,12 +1116,17 @@ typedef enum : NSUInteger { switch (messageItem.messageType) { case TSOutgoingMessageAdapter: { - TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)messageItem; + TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)interaction; if (outgoingMessage.messageState == TSOutgoingMessageStateUnsent) { - [self handleUnsentMessageTap:(TSOutgoingMessage *)interaction]; + [self handleUnsentMessageTap:outgoingMessage]; + + // This `break` is intentionally within the if. + // We want to activate fullscreen media view for sent items + // but not those which failed-to-send + break; } + // No `break` as we want to fall through to capture tapping on Outgoing media items too } - // No `break` as we want to fall through to capture tapping on media items case TSIncomingMessageAdapter: { BOOL isMediaMessage = [messageItem isMediaMessage]; @@ -1306,7 +1326,17 @@ typedef enum : NSUInteger { // FIXME possible for pointer to get stuck in isDownloading state if app is closed while downloading. // see: https://github.com/WhisperSystems/Signal-iOS/issues/1254 if (!pointer.isDownloading) { - [[TSMessagesManager sharedManager] retrieveAttachment:pointer messageId:message.uniqueId]; + OWSAttachmentsProcessor *processor = + [[OWSAttachmentsProcessor alloc] initWithAttachmentPointer:pointer + networkManager:self.networkManager]; + [processor fetchAttachmentsForMessage:message + success:^(TSAttachmentStream *_Nonnull attachmentStream) { + DDLogInfo( + @"%@ Successfully redownloaded attachment in thread: %@", self.tag, message.thread); + } + failure:^(NSError *_Nonnull error) { + DDLogWarn(@"%@ Failed to redownload message with error: %@", self.tag, error); + }]; } } } @@ -1401,25 +1431,25 @@ typedef enum : NSUInteger { - (void)handleUnsentMessageTap:(TSOutgoingMessage *)message { [self dismissKeyBoard]; [DJWActionSheet showInView:self.parentViewController.view - withTitle:nil + withTitle:message.mostRecentFailureText cancelButtonTitle:NSLocalizedString(@"TXT_CANCEL_TITLE", @"") destructiveButtonTitle:NSLocalizedString(@"TXT_DELETE_TITLE", @"") otherButtonTitles:@[ NSLocalizedString(@"SEND_AGAIN_BUTTON", @"") ] tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { - if (tappedButtonIndex == actionSheet.cancelButtonIndex) { - DDLogDebug(@"User Cancelled"); - } else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { - [self.editingDatabaseConnection - readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { - [message removeWithTransaction:transaction]; - }]; - } else { - [[TSMessagesManager sharedManager] sendMessage:message - inThread:self.thread - success:nil - failure:nil]; - [self finishSendingMessage]; - } + if (tappedButtonIndex == actionSheet.cancelButtonIndex) { + DDLogDebug(@"%@ User cancelled unsent dialog", self.tag); + } else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { + DDLogInfo(@"%@ User chose to delete unsent message.", self.tag); + [message remove]; + } else { + [self.messageSender sendMessage:message + success:^{ + DDLogInfo(@"%@ Successfully resent failed message.", self.tag); + } + failure:^(NSError *_Nonnull error) { + DDLogWarn(@"%@ Failed to send message with error: %@", self.tag, error); + }]; + } }]; } @@ -1459,7 +1489,21 @@ typedef enum : NSUInteger { break; case 1: DDLogInfo(@"%@ Remote Key Changed actions: Accepted new identity key", self.tag); + [errorMessage acceptNewIdentityKey]; + if ([errorMessage isKindOfClass:[TSInvalidIdentityKeySendingErrorMessage class]]) { + [self.messageSender + resendMessageFromKeyError:(TSInvalidIdentityKeySendingErrorMessage *) + errorMessage + success:^{ + DDLogDebug(@"%@ Successfully resent key-error message.", self.tag); + } + failure:^(NSError *_Nonnull error) { + DDLogError(@"%@ Failed to resend key-error message with error:%@", + self.tag, + error); + }]; + } break; default: DDLogInfo(@"%@ Remote Key Changed actions: Unhandled button pressed: %d", @@ -1564,7 +1608,7 @@ typedef enum : NSUInteger { // Video picked from library or captured with camera NSURL *videoURL = info[UIImagePickerControllerMediaURL]; - [self sendQualityAdjustedAttachment:videoURL]; + [self sendQualityAdjustedAttachmentForVideo:videoURL]; } else if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) { // Static Image captured from camera @@ -1649,13 +1693,16 @@ typedef enum : NSUInteger { DDLogVerbose(@"Sending attachment. Size in bytes: %lu, contentType: %@", (unsigned long)attachmentData.length, attachmentType); - - [[TSMessagesManager sharedManager] sendAttachment:attachmentData - contentType:attachmentType - inMessage:message - thread:self.thread - success:nil - failure:nil]; + [self.messageSender sendAttachmentData:attachmentData + contentType:attachmentType + inMessage:message + success:^{ + DDLogDebug(@"%@ Successfully sent message attachment.", self.tag); + } + failure:^(NSError *error) { + DDLogError( + @"%@ Failed to send message attachment with error: %@", self.tag, error); + }]; }]; } @@ -1672,7 +1719,7 @@ typedef enum : NSUInteger { return [NSURL fileURLWithPath:basePath]; } -- (void)sendQualityAdjustedAttachment:(NSURL *)movieURL { +- (void)sendQualityAdjustedAttachmentForVideo:(NSURL *)movieURL { AVAsset *video = [AVAsset assetWithURL:movieURL]; AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetMediumQuality]; @@ -1841,7 +1888,6 @@ typedef enum : NSUInteger { [self.messageAdapterCache removeObjectForKey:collectionKey.key]; } [self.collectionView reloadItemsAtIndexPaths:@[ rowChange.indexPath ]]; - scrollToBottom = YES; break; } } @@ -1854,7 +1900,9 @@ typedef enum : NSUInteger { [self.collectionView reloadData]; } if (scrollToBottom) { - [self scrollToBottomAnimated:YES]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self scrollToBottomAnimated:YES]; + }); } }]; } @@ -1997,24 +2045,19 @@ typedef enum : NSUInteger { - (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath - withSender:(id)sender { - TSMessageAdapter *messageAdapter = [self messageAtIndexPath:indexPath]; - // HACK make sure method exists before calling since messageAtIndexPath doesn't - // always return TSMessageAdapters - it can also return JSQCall! - if ([messageAdapter respondsToSelector:@selector(canPerformEditingAction:)]) { - return [messageAdapter canPerformEditingAction:action]; - } - else { - return NO; - } - + withSender:(id)sender +{ + id messageData = [self messageAtIndexPath:indexPath]; + return [messageData canPerformEditingAction:action]; } - (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath - withSender:(id)sender { - [[self messageAtIndexPath:indexPath] performEditingAction:action]; + withSender:(id)sender +{ + id messageData = [self messageAtIndexPath:indexPath]; + [messageData performEditingAction:action]; } - (void)updateGroupModelTo:(TSGroupModel *)newGroupModel @@ -2037,15 +2080,24 @@ typedef enum : NSUInteger { message.customMessage = updateGroupInfo; }]; - if (newGroupModel.groupImage != nil) { - [[TSMessagesManager sharedManager] sendAttachment:UIImagePNGRepresentation(newGroupModel.groupImage) - contentType:OWSMimeTypeImagePng - inMessage:message - thread:groupThread - success:nil - failure:nil]; + if (newGroupModel.groupImage) { + [self.messageSender sendAttachmentData:UIImagePNGRepresentation(newGroupModel.groupImage) + contentType:OWSMimeTypeImagePng + inMessage:message + success:^{ + DDLogDebug(@"%@ Successfully sent group update with avatar", self.tag); + } + failure:^(NSError *_Nonnull error) { + DDLogError(@"%@ Failed to send group avatar update with error: %@", self.tag, error); + }]; } else { - [[TSMessagesManager sharedManager] sendMessage:message inThread:groupThread success:nil failure:nil]; + [self.messageSender sendMessage:message + success:^{ + DDLogDebug(@"%@ Successfully sent group update", self.tag); + } + failure:^(NSError *_Nonnull error) { + DDLogError(@"%@ Failed to send group update with error: %@", self.tag, error); + }]; } self.thread = groupThread; diff --git a/Signal/src/view controllers/NewGroupViewController.m b/Signal/src/view controllers/NewGroupViewController.m index 5cabf5055..4552b096d 100644 --- a/Signal/src/view controllers/NewGroupViewController.m +++ b/Signal/src/view controllers/NewGroupViewController.m @@ -20,20 +20,50 @@ #import #import #import +#import #import -#import -#import static NSString *const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue"; @interface NewGroupViewController () { NSArray *contacts; } + @property TSGroupThread *thread; +@property (nonatomic, readonly) OWSMessageSender *messageSender; @end + @implementation NewGroupViewController +- (instancetype)init +{ + self = [super init]; + if (!self) { + return self; + } + + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager + storageManager:[TSStorageManager sharedManager] + contactsManager:[Environment getCurrent].contactsManager + contactsUpdater:[Environment getCurrent].contactsUpdater]; + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder +{ + self = [super initWithCoder:aDecoder]; + if (!self) { + return self; + } + + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager + storageManager:[TSStorageManager sharedManager] + contactsManager:[Environment getCurrent].contactsManager + contactsUpdater:[Environment getCurrent].contactsUpdater]; + return self; +} + - (void)configWithThread:(TSGroupThread *)gThread { _thread = gThread; } @@ -124,76 +154,53 @@ static NSString *const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue" self.thread = [TSGroupThread getOrCreateThreadWithGroupModel:model transaction:transaction]; }]; + void (^popToThread)() = ^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self dismissViewControllerAnimated:YES + completion:^{ + [Environment messageGroup:self.thread]; + }]; + + }); + }; + + void (^removeThreadWithError)(NSError *error) = ^(NSError *error) { + [self.thread remove]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self dismissViewControllerAnimated:YES + completion:^{ + SignalAlertView(NSLocalizedString(@"GROUP_CREATING_FAILED", nil), + error.localizedDescription); + }]; + }); + }; + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"GROUP_CREATING", nil) message:nil preferredStyle:UIAlertControllerStyleAlert]; - [self - presentViewController:alertController - animated:YES - completion:^{ - TSOutgoingMessage *message = - [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] - inThread:self.thread - messageBody:@"" - attachmentIds:[NSMutableArray new]]; - message.groupMetaMessage = TSGroupMessageNew; - message.customMessage = NSLocalizedString(@"GROUP_CREATED", nil); - if (model.groupImage != nil) { - [[TSMessagesManager sharedManager] sendAttachment:UIImagePNGRepresentation(model.groupImage) - contentType:OWSMimeTypeImagePng - inMessage:message - thread:self.thread - success:^{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self dismissViewControllerAnimated:YES - completion:^{ - [Environment messageGroup:self.thread]; - }]; - }); - } - failure:^{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self - dismissViewControllerAnimated:YES - completion:^{ - [TSStorageManager.sharedManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction){ - [self.thread removeWithTransaction:transaction]; - }]; + [self presentViewController:alertController + animated:YES + completion:^{ + TSOutgoingMessage *message = + [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] + inThread:self.thread + messageBody:@"" + attachmentIds:[NSMutableArray new]]; - SignalAlertView(NSLocalizedString(@"GROUP_CREATING_FAILED", nil), - NSLocalizedString(@"NETWORK_ERROR_RECOVERY", nil)); - }]; - }); - }]; - } else { - [[TSMessagesManager sharedManager] sendMessage:message - inThread:self.thread - success:^{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self dismissViewControllerAnimated:YES - completion:^{ - [Environment messageGroup:self.thread]; - }]; - }); - } - failure:^{ - dispatch_async(dispatch_get_main_queue(), ^{ - [self - dismissViewControllerAnimated:YES - completion:^{ - [TSStorageManager.sharedManager.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction){ - [self.thread removeWithTransaction:transaction]; - }]; - SignalAlertView(NSLocalizedString(@"GROUP_CREATING_FAILED", nil), - NSLocalizedString(@"NETWORK_ERROR_RECOVERY", nil)); - }]; - }); - - }]; - } - }]; + message.groupMetaMessage = TSGroupMessageNew; + message.customMessage = NSLocalizedString(@"GROUP_CREATED", nil); + if (model.groupImage) { + [self.messageSender sendAttachmentData:UIImagePNGRepresentation(model.groupImage) + contentType:OWSMimeTypeImagePng + inMessage:message + success:popToThread + failure:removeThreadWithError]; + } else { + [self.messageSender sendMessage:message success:popToThread failure:removeThreadWithError]; + } + }]; } diff --git a/Signal/src/view controllers/OWSConversationSettingsTableViewController.m b/Signal/src/view controllers/OWSConversationSettingsTableViewController.m index b9018e480..6fb6eb9c6 100644 --- a/Signal/src/view controllers/OWSConversationSettingsTableViewController.m +++ b/Signal/src/view controllers/OWSConversationSettingsTableViewController.m @@ -9,17 +9,18 @@ #import "OWSContactsManager.h" #import "PhoneNumber.h" #import "ShowGroupMembersViewController.h" -#import "UIUtil.h" #import "UIFont+OWS.h" +#import "UIUtil.h" #import <25519/Curve25519.h> #import #import #import #import #import +#import #import #import -#import +#import #import #import @@ -76,7 +77,7 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM @property (nonatomic, readonly) TSStorageManager *storageManager; @property (nonatomic, readonly) OWSContactsManager *contactsManager; -@property (nonatomic, readonly) TSMessagesManager *messagesManager; +@property (nonatomic, readonly) OWSMessageSender *messageSender; @end @@ -90,8 +91,11 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM } _storageManager = [TSStorageManager sharedManager]; - _contactsManager = [[Environment getCurrent] contactsManager]; - _messagesManager = [TSMessagesManager sharedManager]; + _contactsManager = [Environment getCurrent].contactsManager; + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager + storageManager:_storageManager + contactsManager:_contactsManager + contactsUpdater:[Environment getCurrent].contactsUpdater]; return self; } @@ -104,8 +108,11 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM } _storageManager = [TSStorageManager sharedManager]; - _contactsManager = [[Environment getCurrent] contactsManager]; - _messagesManager = [TSMessagesManager sharedManager]; + _contactsManager = [Environment getCurrent].contactsManager; + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager + storageManager:_storageManager + contactsManager:_contactsManager + contactsUpdater:[Environment getCurrent].contactsUpdater]; return self; } @@ -207,7 +214,7 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM [OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob runWithConfiguration:self.disappearingMessagesConfiguration thread:self.thread - messagesManager:self.messagesManager]; + messageSender:self.messageSender]; } } @@ -352,13 +359,12 @@ static NSString *const OWSConversationSettingsTableViewControllerSegueShowGroupM inThread:gThread messageBody:@""]; message.groupMetaMessage = TSGroupMessageQuit; - [self.messagesManager sendMessage:message - inThread:gThread + [self.messageSender sendMessage:message success:^{ DDLogInfo(@"%@ Successfully left group.", self.tag); } - failure:^{ - DDLogWarn(@"%@ Failed to leave group", self.tag); + failure:^(NSError *error) { + DDLogWarn(@"%@ Failed to leave group with error: %@", self.tag, error); }]; NSMutableArray *newGroupMemberIds = [NSMutableArray arrayWithArray:gThread.groupModel.groupMemberIds]; diff --git a/Signal/src/view controllers/SignalsViewController.m b/Signal/src/view controllers/SignalsViewController.m index 872a8d647..544067439 100644 --- a/Signal/src/view controllers/SignalsViewController.m +++ b/Signal/src/view controllers/SignalsViewController.m @@ -18,12 +18,13 @@ #import "TSAccountManager.h" #import "TSDatabaseView.h" #import "TSGroupThread.h" -#import "TSMessagesManager+sendMessages.h" #import "TSStorageManager.h" #import "VersionMigrations.h" - +#import +#import +#import #import -#import "YapDatabaseViewConnection.h" +#import #define CELL_HEIGHT 72.0f #define HEADER_HEIGHT 44.0f @@ -40,6 +41,8 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; @property (nonatomic, retain) UISegmentedControl *segmentedControl; @property (nonatomic, strong) id previewingContext; @property (nonatomic, readonly) OWSContactsManager *contactsManager; +@property (nonatomic, readonly) TSMessagesManager *messagesManager; +@property (nonatomic, readonly) OWSMessageSender *messageSender; @end @@ -53,6 +56,11 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; } _contactsManager = [Environment getCurrent].contactsManager; + _messagesManager = [TSMessagesManager sharedManager]; + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager + storageManager:[TSStorageManager sharedManager] + contactsManager:_contactsManager + contactsUpdater:[Environment getCurrent].contactsUpdater]; return self; } @@ -65,6 +73,11 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; } _contactsManager = [Environment getCurrent].contactsManager; + _messagesManager = [TSMessagesManager sharedManager]; + _messageSender = [[OWSMessageSender alloc] initWithNetworkManager:[Environment getCurrent].networkManager + storageManager:[TSStorageManager sharedManager] + contactsManager:_contactsManager + contactsUpdater:[Environment getCurrent].contactsUpdater]; return self; } @@ -284,20 +297,19 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; messageBody:@"" attachmentIds:[NSMutableArray new]]; message.groupMetaMessage = TSGroupMessageQuit; - [[TSMessagesManager sharedManager] sendMessage:message - inThread:thread + [self.messageSender sendMessage:message success:^{ - [self dismissViewControllerAnimated:YES - completion:^{ - [self deleteThread:thread]; - }]; + [self dismissViewControllerAnimated:YES + completion:^{ + [self deleteThread:thread]; + }]; } - failure:^{ - [self dismissViewControllerAnimated:YES - completion:^{ - SignalAlertView(NSLocalizedString(@"GROUP_REMOVING_FAILED", nil), - NSLocalizedString(@"NETWORK_ERROR_RECOVERY", nil)); - }]; + failure:^(NSError *error) { + [self dismissViewControllerAnimated:YES + completion:^{ + SignalAlertView(NSLocalizedString(@"GROUP_REMOVING_FAILED", nil), + error.localizedRecoverySuggestion); + }]; }]; } else { [self deleteThread:thread]; @@ -326,7 +338,7 @@ static NSString *const kShowSignupFlowSegue = @"showSignupFlow"; } - (NSNumber *)updateInboxCountLabel { - NSUInteger numberOfItems = [[TSMessagesManager sharedManager] unreadMessagesCount]; + NSUInteger numberOfItems = [self.messagesManager unreadMessagesCount]; NSNumber *badgeNumber = [NSNumber numberWithUnsignedInteger:numberOfItems]; NSString *unreadString = NSLocalizedString(@"WHISPER_NAV_BAR_TITLE", nil); diff --git a/Signal/src/views/OWSOutgoingMessageCollectionViewCell.m b/Signal/src/views/OWSOutgoingMessageCollectionViewCell.m index 4ee17be51..06f924d51 100644 --- a/Signal/src/views/OWSOutgoingMessageCollectionViewCell.m +++ b/Signal/src/views/OWSOutgoingMessageCollectionViewCell.m @@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)prepareForReuse { [super prepareForReuse]; + self.mediaView.alpha = 1.0; self.expirationTimerViewWidthConstraint.constant = 0.0f; } diff --git a/Signal/test/view controllers/Signals/TSMessageAdapters/TSMessageAdapterTest.m b/Signal/test/view controllers/Signals/TSMessageAdapters/TSMessageAdapterTest.m index 2ed5a8210..5326dabd1 100644 --- a/Signal/test/view controllers/Signals/TSMessageAdapters/TSMessageAdapterTest.m +++ b/Signal/test/view controllers/Signals/TSMessageAdapters/TSMessageAdapterTest.m @@ -2,12 +2,10 @@ #import "TSAttachmentStream.h" #import "TSContentAdapters.h" -#import "TSInteraction.h" +#import "TSOutgoingMessage.h" #import #import -static NSString * const kTestingInteractionId = @"some-fake-testing-id"; - @interface TSMessageAdapter (Testing) // expose some private setters for ease of testing setup @@ -19,7 +17,7 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; @interface TSMessageAdapterTest : XCTestCase @property TSMessageAdapter *messageAdapter; -@property TSInteraction *interaction; +@property TSOutgoingMessage *message; @property (readonly) NSData *fakeAudioData; @end @@ -42,11 +40,11 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; { [super setUp]; - self.messageAdapter = [[TSMessageAdapter alloc] init]; + self.message = [[TSOutgoingMessage alloc] initWithTimestamp:1 inThread:nil messageBody:nil]; + [self.message save]; - self.interaction = [[TSInteraction alloc] initWithUniqueId:kTestingInteractionId]; - [self.interaction save]; - self.messageAdapter.interaction = self.interaction; + self.messageAdapter = [TSMessageAdapter new]; + self.messageAdapter.interaction = self.message; } - (void)tearDown @@ -94,7 +92,7 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; - (void)testCanPerformEditingActionWithVideoMessage { - TSAttachmentStream *videoAttachment = [[TSAttachmentStream alloc] initWithIdentifier:@"fake-video-message" encryptionKey:nil contentType:@"video/mp4"]; + TSAttachmentStream *videoAttachment = [[TSAttachmentStream alloc] initWithContentType:@"video/mp4"]; self.messageAdapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:videoAttachment incoming:NO]; XCTAssertTrue([self.messageAdapter canPerformEditingAction:@selector(delete:)]); @@ -107,7 +105,7 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; - (void)testCanPerformEditingActionWithAudioMessage { - TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithIdentifier:@"fake-audio-message" encryptionKey:nil contentType:@"audio/mp3"]; + TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithContentType:@"audio/mp3"]; self.messageAdapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:audioAttachment incoming:NO]; XCTAssertTrue([self.messageAdapter canPerformEditingAction:@selector(delete:)]); @@ -124,53 +122,73 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; - (void)testPerformDeleteEditingActionWithNonMediaMessage { - XCTAssertNotNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); + XCTAssertNotNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); [self.messageAdapter performEditingAction:@selector(delete:)]; - XCTAssertNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); + XCTAssertNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); } - (void)testPerformDeleteActionWithPhotoMessage { - XCTAssertNotNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); + XCTAssertNotNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); self.messageAdapter.mediaItem = [[TSPhotoAdapter alloc] init]; [self.messageAdapter performEditingAction:@selector(delete:)]; - XCTAssertNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); + XCTAssertNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); // TODO assert files are deleted } - (void)testPerformDeleteEditingActionWithAnimatedMessage { - XCTAssertNotNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); + XCTAssertNotNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); self.messageAdapter.mediaItem = [[TSAnimatedAdapter alloc] init]; [self.messageAdapter performEditingAction:@selector(delete:)]; - XCTAssertNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); + XCTAssertNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); // TODO assert files are deleted } - (void)testPerformDeleteEditingActionWithVideoMessage { - XCTAssertNotNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); + XCTAssertNotNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); + + NSError *error; + TSAttachmentStream *videoAttachment = [[TSAttachmentStream alloc] initWithContentType:@"video/mp4"]; + [videoAttachment writeData:[NSData new] error:&error]; + [videoAttachment save]; + + [self.message.attachmentIds addObject:videoAttachment.uniqueId]; + [self.message save]; - TSAttachmentStream *videoAttachment = [[TSAttachmentStream alloc] initWithIdentifier:@"fake-video-message" encryptionKey:nil contentType:@"video/mp4"]; self.messageAdapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:videoAttachment incoming:NO]; + // Sanity Check + XCTAssert([[NSFileManager defaultManager] fileExistsAtPath:videoAttachment.filePath]); + [self.messageAdapter performEditingAction:@selector(delete:)]; - XCTAssertNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); - // TODO assert files are deleted + XCTAssertNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); + XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:videoAttachment.filePath]); } - (void)testPerformDeleteEditingActionWithAudioMessage { - XCTAssertNotNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); + XCTAssertNotNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); + + NSError *error; + TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithContentType:@"audio/mp3"]; + [audioAttachment writeData:[NSData new] error:&error]; + [audioAttachment save]; + + [self.message.attachmentIds addObject:audioAttachment.uniqueId]; + [self.message save]; - TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithIdentifier:@"fake-audio-message" encryptionKey:nil contentType:@"audio/mp3"]; self.messageAdapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:audioAttachment incoming:NO]; + // Sanity Check + XCTAssert([[NSFileManager defaultManager] fileExistsAtPath:audioAttachment.filePath]); + [self.messageAdapter performEditingAction:@selector(delete:)]; - XCTAssertNil([TSInteraction fetchObjectWithUniqueID:kTestingInteractionId]); - // TODO assert files are deleted + XCTAssertNil([TSMessage fetchObjectWithUniqueID:self.message.uniqueId]); + XCTAssertFalse([[NSFileManager defaultManager] fileExistsAtPath:audioAttachment.filePath]); } // Test Copy @@ -201,7 +219,10 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; { // reset the paste board for clean slate test UIPasteboard.generalPasteboard.items = @[]; - TSAttachmentStream *videoAttachment = [[TSAttachmentStream alloc] initWithIdentifier:@"fake-video" data:self.fakeVideoData key:nil contentType:@"video/mp4"]; + + NSError *error; + TSAttachmentStream *videoAttachment = [[TSAttachmentStream alloc] initWithContentType:@"video/mp4"]; + [videoAttachment writeData:self.fakeVideoData error:&error]; self.messageAdapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:videoAttachment incoming:YES]; [self.messageAdapter performEditingAction:@selector(copy:)]; @@ -215,7 +236,9 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; UIPasteboard.generalPasteboard.items = @[]; XCTAssertNil([UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeMP3]); - TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithIdentifier:@"fake-audio-message" data:self.fakeAudioData key:nil contentType:@"audio/mp3"]; + NSError *error; + TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithContentType:@"audio/mp3"]; + [audioAttachment writeData:self.fakeAudioData error:&error]; self.messageAdapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:audioAttachment incoming:NO]; [self.messageAdapter performEditingAction:@selector(copy:)]; @@ -227,7 +250,9 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; UIPasteboard.generalPasteboard.items = @[]; XCTAssertNil([UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeMPEG4Audio]); - TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithIdentifier:@"fake-audio-message" data:self.fakeAudioData key:nil contentType:@"audio/x-m4a"]; + NSError *error; + TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithContentType:@"audio/x-m4a"]; + [audioAttachment writeData:self.fakeAudioData error:&error]; self.messageAdapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:audioAttachment incoming:NO]; [self.messageAdapter performEditingAction:@selector(copy:)]; @@ -239,7 +264,9 @@ static NSString * const kTestingInteractionId = @"some-fake-testing-id"; UIPasteboard.generalPasteboard.items = @[]; XCTAssertNil([UIPasteboard.generalPasteboard dataForPasteboardType:(NSString *)kUTTypeAudio]); - TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithIdentifier:@"fake-audio-message" data:self.fakeAudioData key:nil contentType:@"audio/wav"]; + NSError *error; + TSAttachmentStream *audioAttachment = [[TSAttachmentStream alloc] initWithContentType:@"audio/wav"]; + [audioAttachment writeData:self.fakeAudioData error:&error]; self.messageAdapter.mediaItem = [[TSVideoAttachmentAdapter alloc] initWithAttachment:audioAttachment incoming:NO]; [self.messageAdapter performEditingAction:@selector(copy:)]; diff --git a/Signal/translations/bin/auto-genstrings b/Signal/translations/bin/auto-genstrings index 2800914fb..831b502b6 100755 --- a/Signal/translations/bin/auto-genstrings +++ b/Signal/translations/bin/auto-genstrings @@ -1,5 +1,5 @@ #!/bin/bash -TARGETS="Signal/src Pods/SignalServiceKit Pods/JSQMessagesViewController" +TARGETS="Signal/src ../SignalServiceKit/src Pods/JSQMessagesViewController" TMP="$(mktemp -d)" STRINGFILE="Signal/translations/en.lproj/Localizable.strings" diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index f8ed70bb9bc8b9f005a97000c318ee68fc0e320a..7d41a3aa1a3afd0d3028f2c83dc2de3bbe156775 100644 GIT binary patch delta 278 zcmca~g!#xx<_)aolcN;{CO^yxnA~f_GkLM7m~1?QGlLIZJ??Kz=?@eF{(}52#ClL5o43fr~+jA(SBi=ofzm lM+TS456Zc-&DPh&xdEE8Nx1+3 delta 40 ycmV+@0N4M-$pg~F1F!`*v(GrpHIrK*5R(-*P?L>v7_Jc3P