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
This commit is contained in:
parent
7c32259a92
commit
33f6a95520
2
Podfile
2
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'
|
||||
|
|
23
Podfile.lock
23
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
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.6.2</string>
|
||||
<string>2.6.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
@ -38,7 +38,7 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.6.2.0</string>
|
||||
<string>2.6.3.4</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LOGS_EMAIL</key>
|
||||
|
|
|
@ -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 <SignalServiceKit/OWSDisappearingMessagesJob.h>
|
||||
#import <SignalServiceKit/OWSIncomingMessageReadObserver.h>
|
||||
#import <SignalServiceKit/OWSMessageSender.h>
|
||||
|
||||
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];
|
||||
|
|
|
@ -178,6 +178,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)isOutgoingAndDelivered
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSUInteger)messageHash
|
||||
{
|
||||
return self.hash;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>)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> 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>)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>)logging;
|
||||
|
|
|
@ -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 <SignalServiceKit/ContactsUpdater.h>
|
||||
|
||||
#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>)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>)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>)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.
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#import "Release.h"
|
||||
#import "DiscardingLog.h"
|
||||
#import "PhoneManager.h"
|
||||
#import "PhoneNumberUtil.h"
|
||||
#import "RecentCallManager.h"
|
||||
#import "Release.h"
|
||||
#import <SignalServiceKit/ContactsUpdater.h>
|
||||
#import <SignalServiceKit/TSNetworkManager.h>
|
||||
|
||||
#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>)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 {
|
||||
|
|
|
@ -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 <SignalServiceKit/OWSMessageSender.h>
|
||||
|
||||
#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]) {
|
||||
|
|
|
@ -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 <AddressBookUI/AddressBookUI.h>
|
||||
|
@ -53,11 +51,16 @@
|
|||
#import <JSQSystemSoundPlayer.h>
|
||||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
#import <SignalServiceKit/MimeTypeUtil.h>
|
||||
#import <SignalServiceKit/OWSAttachmentsProcessor.h>
|
||||
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
|
||||
#import <SignalServiceKit/OWSFingerprint.h>
|
||||
#import <SignalServiceKit/OWSFingerprintBuilder.h>
|
||||
#import <SignalServiceKit/OWSMessageSender.h>
|
||||
#import <SignalServiceKit/SignalRecipient.h>
|
||||
#import <SignalServiceKit/TSAccountManager.h>
|
||||
#import <SignalServiceKit/TSInvalidIdentityKeySendingErrorMessage.h>
|
||||
#import <SignalServiceKit/TSMessagesManager.h>
|
||||
#import <SignalServiceKit/TSNetworkManager.h>
|
||||
#import <YapDatabase/YapDatabaseView.h>
|
||||
|
||||
@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<OWSMessageData> currentMessage = [self messageAtIndexPath:indexPath];
|
||||
|
||||
TSMessageAdapter *previousMessage =
|
||||
id<OWSMessageData> 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<OWSMessageData> 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<OWSMessageData> 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<OWSMessageData>)nextOutgoingMessage:(NSIndexPath *)indexPath
|
||||
{
|
||||
id<OWSMessageData> 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<OWSMessageData> 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<OWSMessageData> 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;
|
||||
|
|
|
@ -20,20 +20,50 @@
|
|||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
#import <SignalServiceKit/MimeTypeUtil.h>
|
||||
#import <SignalServiceKit/NSDate+millisecondTimeStamp.h>
|
||||
#import <SignalServiceKit/OWSMessageSender.h>
|
||||
#import <SignalServiceKit/TSAccountManager.h>
|
||||
#import <SignalServiceKit/TSMessagesManager+attachments.h>
|
||||
#import <SignalServiceKit/TSMessagesManager+sendMessages.h>
|
||||
|
||||
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];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 <SignalServiceKit/NSDate+millisecondTimeStamp.h>
|
||||
#import <SignalServiceKit/OWSDisappearingConfigurationUpdateInfoMessage.h>
|
||||
#import <SignalServiceKit/OWSDisappearingMessagesConfiguration.h>
|
||||
#import <SignalServiceKit/OWSFingerprint.h>
|
||||
#import <SignalServiceKit/OWSFingerprintBuilder.h>
|
||||
#import <SignalServiceKit/OWSMessageSender.h>
|
||||
#import <SignalServiceKit/OWSNotifyRemoteOfUpdatedDisappearingConfigurationJob.h>
|
||||
#import <SignalServiceKit/TSGroupThread.h>
|
||||
#import <SignalServiceKit/TSMessagesManager+sendMessages.h>
|
||||
#import <SignalServiceKit/TSOutgoingMessage.h>
|
||||
#import <SignalServiceKit/TSStorageManager.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
|
@ -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];
|
||||
|
|
|
@ -18,12 +18,13 @@
|
|||
#import "TSAccountManager.h"
|
||||
#import "TSDatabaseView.h"
|
||||
#import "TSGroupThread.h"
|
||||
#import "TSMessagesManager+sendMessages.h"
|
||||
#import "TSStorageManager.h"
|
||||
#import "VersionMigrations.h"
|
||||
|
||||
#import <SignalServiceKit/OWSMessageSender.h>
|
||||
#import <SignalServiceKit/TSMessagesManager.h>
|
||||
#import <SignalServiceKit/TSOutgoingMessage.h>
|
||||
#import <YapDatabase/YapDatabaseViewChange.h>
|
||||
#import "YapDatabaseViewConnection.h"
|
||||
#import <YapDatabase/YapDatabaseViewConnection.h>
|
||||
|
||||
#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);
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
self.mediaView.alpha = 1.0;
|
||||
self.expirationTimerViewWidthConstraint.constant = 0.0f;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
#import "TSAttachmentStream.h"
|
||||
#import "TSContentAdapters.h"
|
||||
#import "TSInteraction.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
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:)];
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue