mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Group functionality
• create • send/receive full functionality • basic UI for group updating. TODOS: -group avatars not supported -group update occurrence initiated by Android displayed in thread UI but not yet fully Reviewed-by: @FredericJacobs
This commit is contained in:
parent
c74899661c
commit
333c920e0b
27 changed files with 405 additions and 292 deletions
|
@ -363,14 +363,12 @@
|
|||
B6B096631A1D25ED008BFAA6 /* TSPreKeyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B095F11A1D25ED008BFAA6 /* TSPreKeyManager.m */; };
|
||||
B6B096641A1D25ED008BFAA6 /* TSContactThread.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B095F51A1D25ED008BFAA6 /* TSContactThread.m */; };
|
||||
B6B096651A1D25ED008BFAA6 /* TSGroupThread.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B095F71A1D25ED008BFAA6 /* TSGroupThread.m */; };
|
||||
B6B096671A1D25ED008BFAA6 /* TSGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B095FB1A1D25ED008BFAA6 /* TSGroup.m */; };
|
||||
B6B096681A1D25ED008BFAA6 /* TSRecipient.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B095FD1A1D25ED008BFAA6 /* TSRecipient.m */; };
|
||||
B6B096691A1D25ED008BFAA6 /* TSThread.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B095FF1A1D25ED008BFAA6 /* TSThread.m */; };
|
||||
B6B0966A1A1D25ED008BFAA6 /* IncomingPushMessageSignal.pb.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096021A1D25ED008BFAA6 /* IncomingPushMessageSignal.pb.m */; settings = {COMPILER_FLAGS = "-w"; }; };
|
||||
B6B0966B1A1D25ED008BFAA6 /* TSAttachment.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096041A1D25ED008BFAA6 /* TSAttachment.m */; };
|
||||
B6B0966C1A1D25ED008BFAA6 /* TSCall.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096061A1D25ED008BFAA6 /* TSCall.m */; };
|
||||
B6B0966D1A1D25ED008BFAA6 /* TSErrorMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096081A1D25ED008BFAA6 /* TSErrorMessage.m */; };
|
||||
B6B0966E1A1D25ED008BFAA6 /* TSGroupMessageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B0960A1A1D25ED008BFAA6 /* TSGroupMessageManager.m */; };
|
||||
B6B0966F1A1D25ED008BFAA6 /* TSIncomingMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B0960C1A1D25ED008BFAA6 /* TSIncomingMessage.m */; };
|
||||
B6B096701A1D25ED008BFAA6 /* TSInfoMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B0960E1A1D25ED008BFAA6 /* TSInfoMessage.m */; };
|
||||
B6B096711A1D25ED008BFAA6 /* TSInteraction.m in Sources */ = {isa = PBXBuildFile; fileRef = B6B096101A1D25ED008BFAA6 /* TSInteraction.m */; };
|
||||
|
@ -995,8 +993,6 @@
|
|||
B6B095F51A1D25ED008BFAA6 /* TSContactThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSContactThread.m; sourceTree = "<group>"; };
|
||||
B6B095F61A1D25ED008BFAA6 /* TSGroupThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGroupThread.h; sourceTree = "<group>"; };
|
||||
B6B095F71A1D25ED008BFAA6 /* TSGroupThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGroupThread.m; sourceTree = "<group>"; };
|
||||
B6B095FA1A1D25ED008BFAA6 /* TSGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGroup.h; sourceTree = "<group>"; };
|
||||
B6B095FB1A1D25ED008BFAA6 /* TSGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGroup.m; sourceTree = "<group>"; };
|
||||
B6B095FC1A1D25ED008BFAA6 /* TSRecipient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSRecipient.h; sourceTree = "<group>"; };
|
||||
B6B095FD1A1D25ED008BFAA6 /* TSRecipient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSRecipient.m; sourceTree = "<group>"; };
|
||||
B6B095FE1A1D25ED008BFAA6 /* TSThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSThread.h; path = ../TSThread.h; sourceTree = "<group>"; };
|
||||
|
@ -1009,8 +1005,6 @@
|
|||
B6B096061A1D25ED008BFAA6 /* TSCall.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSCall.m; sourceTree = "<group>"; };
|
||||
B6B096071A1D25ED008BFAA6 /* TSErrorMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSErrorMessage.h; sourceTree = "<group>"; };
|
||||
B6B096081A1D25ED008BFAA6 /* TSErrorMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSErrorMessage.m; sourceTree = "<group>"; };
|
||||
B6B096091A1D25ED008BFAA6 /* TSGroupMessageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSGroupMessageManager.h; sourceTree = "<group>"; };
|
||||
B6B0960A1A1D25ED008BFAA6 /* TSGroupMessageManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSGroupMessageManager.m; sourceTree = "<group>"; };
|
||||
B6B0960B1A1D25ED008BFAA6 /* TSIncomingMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSIncomingMessage.h; sourceTree = "<group>"; };
|
||||
B6B0960C1A1D25ED008BFAA6 /* TSIncomingMessage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TSIncomingMessage.m; sourceTree = "<group>"; };
|
||||
B6B0960D1A1D25ED008BFAA6 /* TSInfoMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TSInfoMessage.h; sourceTree = "<group>"; };
|
||||
|
@ -2335,8 +2329,6 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
B6B095F31A1D25ED008BFAA6 /* Threads */,
|
||||
B6B095FA1A1D25ED008BFAA6 /* TSGroup.h */,
|
||||
B6B095FB1A1D25ED008BFAA6 /* TSGroup.m */,
|
||||
B6B095FC1A1D25ED008BFAA6 /* TSRecipient.h */,
|
||||
B6B095FD1A1D25ED008BFAA6 /* TSRecipient.m */,
|
||||
);
|
||||
|
@ -2366,8 +2358,6 @@
|
|||
B6B096061A1D25ED008BFAA6 /* TSCall.m */,
|
||||
B6B096071A1D25ED008BFAA6 /* TSErrorMessage.h */,
|
||||
B6B096081A1D25ED008BFAA6 /* TSErrorMessage.m */,
|
||||
B6B096091A1D25ED008BFAA6 /* TSGroupMessageManager.h */,
|
||||
B6B0960A1A1D25ED008BFAA6 /* TSGroupMessageManager.m */,
|
||||
B6B0960B1A1D25ED008BFAA6 /* TSIncomingMessage.h */,
|
||||
B6B0960C1A1D25ED008BFAA6 /* TSIncomingMessage.m */,
|
||||
B6B0960D1A1D25ED008BFAA6 /* TSInfoMessage.h */,
|
||||
|
@ -3107,7 +3097,6 @@
|
|||
76EB061A18170B33006006FC /* DiscardingLog.m in Sources */,
|
||||
B63AF5C71A1F757900D01AAD /* TSContactsIntersectionRequest.m in Sources */,
|
||||
B6B0968B1A1D25ED008BFAA6 /* TSStorageManager+SignedPreKeyStore.m in Sources */,
|
||||
B6B0966E1A1D25ED008BFAA6 /* TSGroupMessageManager.m in Sources */,
|
||||
76EB05AC18170B33006006FC /* SrtpSocket.m in Sources */,
|
||||
B6CBF53F1A254BD1000D4184 /* ContactDetailCell.m in Sources */,
|
||||
FCB11D931A12A4AA002F93FB /* FullImageViewController.m in Sources */,
|
||||
|
@ -3150,7 +3139,6 @@
|
|||
76EB063A18170B33006006FC /* FunctionalUtil.m in Sources */,
|
||||
76EB060A18170B33006006FC /* SignalUtil.m in Sources */,
|
||||
76EB062818170B33006006FC /* BadArgument.m in Sources */,
|
||||
B6B096671A1D25ED008BFAA6 /* TSGroup.m in Sources */,
|
||||
76EB062E18170B33006006FC /* SecurityFailure.m in Sources */,
|
||||
76EB05F218170B33006006FC /* PhoneNumber.m in Sources */,
|
||||
E197B61718BBEC1A00F073E5 /* AnonymousAudioCallbackHandler.m in Sources */,
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
</view>
|
||||
<connections>
|
||||
<segue destination="urv-62-RsD" kind="presentation" identifier="fingerprintSegue" modalPresentationStyle="overCurrentContext" animates="NO" id="Zjl-QX-tHE"/>
|
||||
<segue destination="bDi-2Q-XOC" kind="show" identifier="updateGroupSegue" id="gZ1-lh-srF"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="yXZ-iE-5va" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
@ -3404,7 +3405,7 @@ Licensed under the GPLv3</string>
|
|||
<placeholder placeholderIdentifier="IBFirstResponder" id="fUD-iU-Cax" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<searchDisplayController id="f1M-Dk-nMv"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3082.5" y="-906.75"/>
|
||||
<point key="canvasLocation" x="3184.5" y="-906.75"/>
|
||||
</scene>
|
||||
<!--New Group View Controller-->
|
||||
<scene sceneID="mdV-ti-fPA">
|
||||
|
@ -3612,7 +3613,7 @@ Licensed under the GPLv3</string>
|
|||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="GsM-dR-L7j" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3930" y="-906.75"/>
|
||||
<point key="canvasLocation" x="4197" y="-899.25"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="kfT-eG-hkf">
|
||||
|
@ -3657,4 +3658,7 @@ Licensed under the GPLv3</string>
|
|||
<image name="signal.png" width="50" height="50"/>
|
||||
<image name="signals_tab.png" width="24" height="24"/>
|
||||
</resources>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="gZ1-lh-srF"/>
|
||||
</inferredMetricsTieBreakers>
|
||||
</document>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#import "PropertyListPreferences.h"
|
||||
#import "PacketHandler.h"
|
||||
#import "SecureEndPoint.h"
|
||||
|
||||
#import "GroupModel.h"
|
||||
/**
|
||||
*
|
||||
* Environment is a data and data accessor class.
|
||||
|
@ -85,5 +85,7 @@ andCurrentRegionCodeForPhoneNumbers:(NSString*)currentRegionCodeForPhoneNumbers
|
|||
- (void)setSignUpFlowNavigationController:(UINavigationController *)signUpFlowNavigationController;
|
||||
|
||||
+ (void)messageIdentifier:(NSString*)identifier;
|
||||
+ (void)groupModel:(GroupModel*)model;
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -189,6 +189,26 @@ phoneDirectoryManager;
|
|||
}
|
||||
}
|
||||
|
||||
+ (void)groupModel:(GroupModel*)model {
|
||||
Environment *env = [self getCurrent];
|
||||
SignalsViewController *vc = env.signalsViewController;
|
||||
|
||||
if (vc.presentedViewController) {
|
||||
[vc.presentedViewController dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
[vc.navigationController popToRootViewControllerAnimated:YES];
|
||||
vc.groupFromCompose = model;
|
||||
[vc performSegueWithIdentifier:@"showSegue" sender:nil];
|
||||
|
||||
UITabBarController *tabBarController = (UITabBarController*)vc.parentViewController.parentViewController;
|
||||
if ([tabBarController respondsToSelector:@selector(selectedIndex)]) {
|
||||
tabBarController.selectedIndex = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+ (void)resetAppData{
|
||||
[SignalKeyingStorage wipeKeychain];
|
||||
[Environment.preferences clear];
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
//
|
||||
// TSGroup.h
|
||||
// TextSecureKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 12/11/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TSAttachment.h"
|
||||
|
||||
#import "TSYapDatabaseObject.h"
|
||||
|
||||
@interface TSGroup : TSYapDatabaseObject
|
||||
|
||||
@property (nonatomic) NSString *name;// Name of the group
|
||||
@property (nonatomic) TSAttachment *avatar;// Link to the attachment object (group picture)
|
||||
@property (nonatomic) NSSet *members;// Each member of the discussion is a TSUser
|
||||
|
||||
- (NSData*)groupIdentifier;
|
||||
|
||||
+ (TSGroup*)groupWithId:(NSData*)id;
|
||||
|
||||
- (NSSet*)membersIdentifier;
|
||||
|
||||
|
||||
@end
|
|
@ -1,17 +0,0 @@
|
|||
//
|
||||
// TSGroup.m
|
||||
// TextSecureKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 12/11/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TSGroup.h"
|
||||
|
||||
@implementation TSGroup
|
||||
|
||||
+ (NSString*)collection{
|
||||
return @"TSGroup";
|
||||
}
|
||||
|
||||
@end
|
|
@ -11,7 +11,6 @@
|
|||
#import "ContactsManager.h"
|
||||
#import "TSInteraction.h"
|
||||
#import "TSStorageManager.h"
|
||||
#import "TSGroup.h"
|
||||
|
||||
#import "TSCall.h"
|
||||
#import "TSOutgoingMessage.h"
|
||||
|
|
|
@ -7,11 +7,13 @@
|
|||
//
|
||||
|
||||
#import "TSThread.h"
|
||||
#import "GroupModel.h"
|
||||
|
||||
@interface TSGroupThread : TSThread
|
||||
|
||||
+ (instancetype)threadWithGroupId:(NSData*)groupId;
|
||||
@property (nonatomic,strong) GroupModel* groupModel;
|
||||
+ (instancetype)threadWithGroupModel:(GroupModel *)groupModel transaction:(YapDatabaseReadWriteTransaction*)transaction;
|
||||
|
||||
- (NSData*)groupId;
|
||||
- (NSArray *)recipientsWithTransaction:(YapDatabaseReadTransaction*)transaction;
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,30 +7,34 @@
|
|||
//
|
||||
|
||||
#import "TSGroupThread.h"
|
||||
#import "TSRecipient.h"
|
||||
#import "NSData+Base64.h"
|
||||
|
||||
@implementation TSGroupThread
|
||||
|
||||
#define TSGroupThreadPrefix @"g"
|
||||
|
||||
- (instancetype)initWithGroupId:(NSData*)groupId{
|
||||
- (instancetype)initWithGroupModel:(GroupModel *)groupModel{
|
||||
|
||||
NSString *uniqueIdentifier = [[self class] threadIdFromGroupId:groupId];
|
||||
NSString *uniqueIdentifier = [[self class] threadIdFromGroupId:groupModel.groupId];
|
||||
|
||||
self = [super initWithUniqueId:uniqueIdentifier];
|
||||
|
||||
_groupModel = groupModel;
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)threadWithGroupId:(NSData *)groupId{
|
||||
|
||||
TSGroupThread *thread = [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupId]];
|
||||
|
||||
|
||||
+ (instancetype)threadWithGroupModel:(GroupModel *)groupModel transaction:(YapDatabaseReadWriteTransaction*)transaction{
|
||||
TSGroupThread *thread = [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupModel.groupId] transaction:transaction];
|
||||
|
||||
if (!thread) {
|
||||
thread = [[TSGroupThread alloc] initWithGroupId:groupId];
|
||||
[thread save];
|
||||
thread = [[TSGroupThread alloc] initWithGroupModel:groupModel];
|
||||
[thread saveWithTransaction:transaction];
|
||||
}
|
||||
else if(![thread.groupModel isEqual:groupModel]) {
|
||||
thread.groupModel = groupModel;
|
||||
[thread saveWithTransaction:transaction];
|
||||
}
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
|
@ -42,6 +46,12 @@
|
|||
return [[self class] groupIdFromThreadId:self.uniqueId];
|
||||
}
|
||||
|
||||
|
||||
- (NSString*)name{
|
||||
return self.groupModel.groupName;
|
||||
}
|
||||
|
||||
|
||||
+ (NSString*)threadIdFromGroupId:(NSData*)groupId{
|
||||
return [TSGroupThreadPrefix stringByAppendingString:[groupId base64EncodedString]];
|
||||
}
|
||||
|
@ -50,4 +60,20 @@
|
|||
return [NSData dataFromBase64String:[threadId substringWithRange:NSMakeRange(1, threadId.length-1)]];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (NSArray *)recipientsWithTransaction:(YapDatabaseReadTransaction*)transaction{
|
||||
NSMutableArray *recipients = [[NSMutableArray alloc] init];
|
||||
|
||||
for(NSString *recipientId in _groupModel.groupMemberIds) {
|
||||
TSRecipient *recipient = [TSRecipient recipientWithTextSecureIdentifier:recipientId withTransaction:transaction];
|
||||
if (!recipient){
|
||||
recipient = [[TSRecipient alloc] initWithTextSecureIdentifier:recipientId relay:nil];
|
||||
}
|
||||
[recipients addObject:recipient];
|
||||
}
|
||||
return recipients;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
//
|
||||
// TSGroupMessageManager.h
|
||||
// TextSecureKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 15/11/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "IncomingPushMessageSignal.pb.h"
|
||||
|
||||
@interface TSGroupMessageManager : NSObject
|
||||
|
||||
+ (void)processGroupMessage:(IncomingPushMessageSignal*)pushMessage content:(PushMessageContent*)content;
|
||||
|
||||
@end
|
|
@ -1,88 +0,0 @@
|
|||
//
|
||||
// TSGroupMessageManager.m
|
||||
// TextSecureKit
|
||||
//
|
||||
// Created by Frederic Jacobs on 15/11/14.
|
||||
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <CocoaLumberjack/DDLog.h>
|
||||
|
||||
#import "TSGroup.h"
|
||||
#import "TSGroupMessageManager.h"
|
||||
|
||||
#define ddLogLevel LOG_LEVEL_VERBOSE
|
||||
|
||||
|
||||
@implementation TSGroupMessageManager
|
||||
|
||||
+ (void)processGroupMessage:(IncomingPushMessageSignal*)pushMessage content:(PushMessageContent*)content{
|
||||
if (!content.group.id) {
|
||||
DDLogInfo(@"Received group message with no id! Ignoring...");
|
||||
return;
|
||||
}
|
||||
|
||||
PushMessageContentGroupContext *group = content.group;
|
||||
NSData *id = group.id;
|
||||
int type = group.type;
|
||||
TSGroup *record = [TSGroup groupWithId:id];
|
||||
|
||||
if (record != nil && type == PushMessageContentGroupContextTypeUpdate) {
|
||||
//TODO: [self handleGroupUpdate:pushMessage group:group record:record];
|
||||
} else if (record == nil && type == PushMessageContentGroupContextTypeUpdate) {
|
||||
[self handleGroupCreate:pushMessage group:group record:record];
|
||||
} else if (record != nil && type == PushMessageContentGroupContextTypeQuit) {
|
||||
[self handleGroupLeave:pushMessage group:group record:record];
|
||||
} else if (type == PushMessageContentGroupContextTypeUnknown) {
|
||||
DDLogInfo(@"Received unknown type, ignoring...");
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)handleGroupCreate:(IncomingPushMessageSignal*)message group:(PushMessageContentGroupContext*)group record:(TSGroup*)record{
|
||||
//TODO
|
||||
}
|
||||
|
||||
+ (void)handleGroupLeave:(IncomingPushMessageSignal*)pushMessage group:(PushMessageContentGroupContext*)group record:(TSGroup*)record{
|
||||
//TODO
|
||||
}
|
||||
|
||||
|
||||
//+ (void)handleGroupUpdate:(IncomingPushMessageSignal*)pushMessage group:(PushMessageContentGroupContext*)group record:(TSGroup*)record{
|
||||
// NSData *identifier = group.id;
|
||||
// NSArray *messageMembersIds = group.members;
|
||||
//
|
||||
// NSSet *recordMembers = record.membersIdentifier;
|
||||
// NSSet *messageMembers = [NSSet setWithArray:messageMembersIds];
|
||||
//
|
||||
// NSMutableSet *addedMembers = [messageMembers mutableCopy];
|
||||
// [addedMembers minusSet:recordMembers];
|
||||
//
|
||||
// NSMutableSet missingMembers = [recordMembers mutableCopy];
|
||||
// [missingMembers minusSet:messageMembers];
|
||||
//
|
||||
// if (addedMembers.count > 0) {
|
||||
// Set<String> unionMembers = new HashSet<String>(recordMembers);
|
||||
// unionMembers.addAll(messageMembers);
|
||||
// database.updateMembers(id, new LinkedList<String>(unionMembers));
|
||||
//
|
||||
// group = group.toBuilder().clearMembers().addAllMembers(addedMembers).build();
|
||||
//
|
||||
// } else {
|
||||
// group = group.toBuilder().clearMembers().build();
|
||||
// }
|
||||
//
|
||||
// if (missingMembers > 0) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// if (group.hasName || group.hasAvatar) {
|
||||
// record.avatar = group.avatar;
|
||||
// record.name = group.name;
|
||||
// [record save];
|
||||
// }
|
||||
//
|
||||
// // TO-DO: Implement
|
||||
//
|
||||
//}
|
||||
|
||||
@end
|
|
@ -13,13 +13,14 @@
|
|||
typedef NS_ENUM(NSInteger, TSInfoMessageType){
|
||||
TSInfoMessageTypeSessionDidEnd,
|
||||
TSInfoMessageUserNotRegistered,
|
||||
TSInfoMessageTypeUnsupportedMessage
|
||||
TSInfoMessageTypeUnsupportedMessage,
|
||||
TSInfoMessageTypeGroupUpdate
|
||||
};
|
||||
|
||||
+ (instancetype)userNotRegisteredMessageInThread:(TSThread*)thread transaction:(YapDatabaseReadWriteTransaction*)transaction;
|
||||
|
||||
@property TSInfoMessageType messageType;
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSContactThread *)contact messageType:(TSInfoMessageType)infoMessage;
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)contact messageType:(TSInfoMessageType)infoMessage;
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
@implementation TSInfoMessage
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSContactThread *)contact messageType:(TSInfoMessageType)infoMessage{
|
||||
self = [super initWithTimestamp:timestamp inThread:contact messageBody:nil attachments:nil];
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread messageType:(TSInfoMessageType)infoMessage{
|
||||
self = [super initWithTimestamp:timestamp inThread:thread messageBody:nil attachments:nil];
|
||||
|
||||
if (self) {
|
||||
_messageType = infoMessage;
|
||||
|
@ -21,7 +21,7 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)userNotRegisteredMessageInThread:(TSContactThread*)thread transaction:(YapDatabaseReadWriteTransaction*)transaction{
|
||||
+ (instancetype)userNotRegisteredMessageInThread:(TSThread*)thread transaction:(YapDatabaseReadWriteTransaction*)transaction{
|
||||
return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageUserNotRegistered];
|
||||
|
||||
}
|
||||
|
@ -34,6 +34,8 @@
|
|||
return @"Media messages are currently not supported.";
|
||||
case TSInfoMessageUserNotRegistered:
|
||||
return @"The user is not registered.";
|
||||
case TSInfoMessageTypeGroupUpdate:
|
||||
return @"Updated the group";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -13,10 +13,18 @@
|
|||
* Abstract message class. Is instantiated by either
|
||||
*/
|
||||
|
||||
typedef NS_ENUM(NSInteger, TSGroupMetaMessage){
|
||||
TSGroupMessageNone,
|
||||
TSGroupMessageNew,
|
||||
TSGroupMessageUpdate,
|
||||
TSGroupMessageDeliver,
|
||||
TSGroupMessageQuit
|
||||
};
|
||||
@interface TSMessage : TSInteraction
|
||||
|
||||
@property (nonatomic, readonly) NSMutableArray *attachments;
|
||||
@property (nonatomic, readonly) NSString *body;
|
||||
@property (nonatomic) TSGroupMetaMessage groupMetaMessage;
|
||||
|
||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||
inThread:(TSThread*)thread
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#import "TSStorageManager+SignedPreKeyStore.h"
|
||||
|
||||
#import "PreKeyBundle+jsonDict.h"
|
||||
#import "SignalKeyingStorage.h"
|
||||
|
||||
#import "TSAttachmentStream.h"
|
||||
#import "TSNetworkManager.h"
|
||||
|
@ -53,12 +54,30 @@ dispatch_queue_t sendingQueue() {
|
|||
}
|
||||
|
||||
- (void)sendMessage:(TSOutgoingMessage*)message inThread:(TSThread*)thread{
|
||||
[self saveMessage:message withState:TSOutgoingMessageStateAttemptingOut];
|
||||
|
||||
dispatch_async(sendingQueue(), ^{
|
||||
if ([thread isKindOfClass:[TSGroupThread class]]) {
|
||||
NSLog(@"Currently unsupported");
|
||||
} else if([thread isKindOfClass:[TSContactThread class]]){
|
||||
TSGroupThread* groupThread = (TSGroupThread*)thread;
|
||||
|
||||
[self saveGroupMessage:message inThread:thread];
|
||||
__block NSArray* recipients;
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
recipients = [groupThread recipientsWithTransaction:transaction];
|
||||
}];
|
||||
|
||||
for(TSRecipient *rec in recipients){
|
||||
// TODOGROUP hack so that we don't send group messages to ourselves; probably a more elegant way of doing this.
|
||||
if( ![[rec uniqueId] isEqualToString:[SignalKeyingStorage.localNumber toE164]]){
|
||||
[self sendMessage:message
|
||||
toRecipient:rec
|
||||
inThread:thread
|
||||
withAttemps:3];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if([thread isKindOfClass:[TSContactThread class]]){
|
||||
[self saveMessage:message withState:TSOutgoingMessageStateDelivered];
|
||||
|
||||
TSContactThread *contactThread = (TSContactThread*)thread;
|
||||
__block TSRecipient *recipient;
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
|
@ -81,7 +100,7 @@ dispatch_queue_t sendingQueue() {
|
|||
if (remainingAttempts > 0) {
|
||||
remainingAttempts -= 1;
|
||||
|
||||
[self outgoingMessages:message toRecipient:recipient completion:^(NSArray *messages) {
|
||||
[self outgoingMessages:message toRecipient:recipient inThread:thread completion:^(NSArray *messages) {
|
||||
TSSubmitMessageRequest *request = [[TSSubmitMessageRequest alloc] initWithRecipient:recipient.uniqueId messages:messages relay:recipient.relay timeStamp:message.timeStamp];
|
||||
|
||||
[[TSNetworkManager sharedManager] queueAuthenticatedRequest:request success:^(NSURLSessionDataTask *task, id responseObject) {
|
||||
|
@ -127,10 +146,10 @@ dispatch_queue_t sendingQueue() {
|
|||
[self saveMessage:message withState:TSOutgoingMessageStateSent];
|
||||
}
|
||||
|
||||
- (void)outgoingMessages:(TSOutgoingMessage*)message toRecipient:(TSRecipient*)recipient completion:(messagesQueue)sendMessages{
|
||||
- (void)outgoingMessages:(TSOutgoingMessage*)message toRecipient:(TSRecipient*)recipient inThread:(TSThread*)thread completion:(messagesQueue)sendMessages{
|
||||
NSMutableArray *messagesArray = [NSMutableArray arrayWithCapacity:recipient.devices.count];
|
||||
TSStorageManager *storage = [TSStorageManager sharedManager];
|
||||
NSData *plainText = [self plainTextForMessage:message];
|
||||
NSData *plainText = [self plainTextForMessage:message inThread:thread];
|
||||
|
||||
for (NSNumber *deviceNumber in recipient.devices) {
|
||||
@try {
|
||||
|
@ -209,16 +228,51 @@ dispatch_queue_t sendingQueue() {
|
|||
}
|
||||
|
||||
- (void)saveMessage:(TSOutgoingMessage*)message withState:(TSOutgoingMessageState)state{
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[message setMessageState:state];
|
||||
[message saveWithTransaction:transaction];
|
||||
}];
|
||||
if(message.groupMetaMessage == TSGroupMessageDeliver || message.groupMetaMessage == TSGroupMessageNone) {
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[message setMessageState:state];
|
||||
[message saveWithTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData*)plainTextForMessage:(TSOutgoingMessage*)message{
|
||||
|
||||
- (void) saveGroupMessage:(TSOutgoingMessage*)message inThread:(TSThread*)thread{
|
||||
if(message.groupMetaMessage==TSGroupMessageDeliver) {
|
||||
[self saveMessage:message withState:message.messageState];
|
||||
}
|
||||
else {
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
|
||||
[[[TSInfoMessage alloc] initWithTimestamp:message.timeStamp inThread:thread messageType:TSInfoMessageTypeGroupUpdate] saveWithTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData*)plainTextForMessage:(TSOutgoingMessage*)message inThread:(TSThread*)thread{
|
||||
PushMessageContentBuilder *builder = [PushMessageContentBuilder new];
|
||||
[builder setBody:message.body];
|
||||
|
||||
if([thread isKindOfClass:[TSGroupThread class]]) {
|
||||
TSGroupThread *gThread = (TSGroupThread*)thread;
|
||||
PushMessageContentGroupContextBuilder *groupBuilder = [PushMessageContentGroupContextBuilder new];
|
||||
[groupBuilder setMembersArray:gThread.groupModel.groupMemberIds];
|
||||
[groupBuilder setName:gThread.groupModel.groupName];
|
||||
[groupBuilder setId:gThread.groupModel.groupId];
|
||||
switch (message.groupMetaMessage) {
|
||||
case TSGroupMessageQuit:
|
||||
[groupBuilder setType:PushMessageContentGroupContextTypeQuit];
|
||||
break;
|
||||
case TSGroupMessageUpdate:
|
||||
case TSGroupMessageNew:
|
||||
[groupBuilder setType:PushMessageContentGroupContextTypeUpdate];
|
||||
break;
|
||||
default:
|
||||
[groupBuilder setType:PushMessageContentGroupContextTypeDeliver];
|
||||
break;
|
||||
}
|
||||
//[groupBuilder setAvatar:(PushMessageContentAttachmentPointer *)]; //TODOATTACHMENTS for avatar
|
||||
[builder setGroup:groupBuilder.build];
|
||||
}
|
||||
|
||||
NSMutableArray *attachmentsArray = [NSMutableArray array];
|
||||
|
||||
|
|
|
@ -98,8 +98,10 @@
|
|||
- (void)handleDeliveryReceipt:(IncomingPushMessageSignal*)signal{
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
TSOutgoingMessage *message = [TSOutgoingMessage fetchObjectWithUniqueID:[TSInteraction stringFromTimeStamp:signal.timestamp] transaction:transaction];
|
||||
message.messageState = TSOutgoingMessageStateDelivered;
|
||||
[message saveWithTransaction:transaction];
|
||||
if(![message isKindOfClass:[TSInfoMessage class]]){
|
||||
message.messageState = TSOutgoingMessageStateDelivered;
|
||||
[message saveWithTransaction:transaction];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
@ -179,14 +181,11 @@
|
|||
if ((content.flags & PushMessageContentFlagsEndSession) != 0) {
|
||||
DDLogVerbose(@"Received end session message...");
|
||||
[self handleEndSessionMessage:incomingMessage withContent:content];
|
||||
} else if (content.hasGroup && (content.group.type != PushMessageContentGroupContextTypeDeliver)) {
|
||||
DDLogVerbose(@"Received push group update message...");
|
||||
[self handleGroupMessage:incomingMessage withContent:content];
|
||||
} else if (content.attachments.count > 0) {
|
||||
DDLogVerbose(@"Received push media message (attachment) ...");
|
||||
[self handleReceivedMediaMessage:incomingMessage withContent:content];
|
||||
} else {
|
||||
DDLogVerbose(@"Received push text message...");
|
||||
DDLogVerbose(@"Received individual push text message...");
|
||||
[self handleReceivedTextMessage:incomingMessage withContent:content];
|
||||
}
|
||||
}
|
||||
|
@ -204,12 +203,8 @@
|
|||
[[TSStorageManager sharedManager] deleteAllSessionsForContact:message.source];
|
||||
}
|
||||
|
||||
- (void)handleGroupMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content{
|
||||
// TO DO
|
||||
}
|
||||
|
||||
- (void)handleReceivedTextMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content{
|
||||
[self handleReceivedMessage:message withContent:content attachments:nil];
|
||||
[self handleReceivedMessage:message withContent:content attachments:content.attachments];
|
||||
}
|
||||
|
||||
- (void)handleReceivedMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content attachments:(NSArray*)attachments {
|
||||
|
@ -221,17 +216,25 @@
|
|||
TSIncomingMessage *incomingMessage;
|
||||
TSThread *thread;
|
||||
if (groupId) {
|
||||
TSGroupThread *gThread = [TSGroupThread threadWithGroupId:groupId];
|
||||
[gThread saveWithTransaction:transaction];
|
||||
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp inThread:gThread authorId:message.source messageBody:body attachments:attachments];
|
||||
GroupModel *model = [[GroupModel alloc] initWithTitle:content.group.name memberIds:[[NSMutableArray alloc ] initWithArray:content.group.members] image:nil groupId:content.group.id]; //TODOGROUP group avatar will not be nil generically
|
||||
TSGroupThread *gThread = [TSGroupThread threadWithGroupModel:model transaction:transaction];
|
||||
[gThread saveWithTransaction:transaction];
|
||||
if(content.group.type==PushMessageContentGroupContextTypeUpdate) {
|
||||
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp inThread:gThread messageType:TSInfoMessageTypeGroupUpdate] saveWithTransaction:transaction];
|
||||
}
|
||||
else {
|
||||
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp inThread:gThread authorId:message.source messageBody:body attachments:attachments];
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
}
|
||||
thread = gThread;
|
||||
} else{
|
||||
}
|
||||
else{
|
||||
TSContactThread *cThread = [TSContactThread threadWithContactId:message.source transaction:transaction];
|
||||
[cThread saveWithTransaction:transaction];
|
||||
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp inThread:cThread messageBody:body attachments:attachments];
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
thread = cThread;
|
||||
}
|
||||
[incomingMessage saveWithTransaction:transaction];
|
||||
NSString *name = [thread name];
|
||||
[self notifyUserForIncomingMessage:incomingMessage from:name];
|
||||
}];
|
||||
|
@ -266,10 +269,13 @@
|
|||
|
||||
- (void)processException:(NSException*)exception outgoingMessage:(TSOutgoingMessage*)message{
|
||||
DDLogWarn(@"Got exception: %@", exception.description);
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[message setMessageState:TSOutgoingMessageStateUnsent];
|
||||
[message saveWithTransaction:transaction];
|
||||
}];
|
||||
if(message.groupMetaMessage==TSGroupMessageNone) {
|
||||
// Only update this with exception if it is not a group message as group messages may except for one group send but not another and the UI doesn't know how to handle that
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[message setMessageState:TSOutgoingMessageStateUnsent];
|
||||
[message saveWithTransaction:transaction];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyUserForIncomingMessage:(TSIncomingMessage*)message from:(NSString*)name{
|
||||
|
|
|
@ -18,5 +18,4 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState){
|
|||
};
|
||||
|
||||
@property (nonatomic) TSOutgoingMessageState messageState;
|
||||
|
||||
@end
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
|
||||
if (self) {
|
||||
_messageState = TSOutgoingMessageStateAttemptingOut;
|
||||
if( [thread isKindOfClass:[TSGroupThread class]]) {
|
||||
self.groupMetaMessage = TSGroupMessageDeliver;
|
||||
}
|
||||
else {
|
||||
self.groupMetaMessage = TSGroupMessageNone;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
|
|
|
@ -7,14 +7,20 @@
|
|||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface GroupModel : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray * groupMembers;
|
||||
@property (nonatomic, strong) UIImage * groupImage;
|
||||
@property (nonatomic, strong) NSString * groupName;
|
||||
#import "TSYapDatabaseObject.h"
|
||||
|
||||
|
||||
-(instancetype)initWithTitle:(NSString*)title members:(NSMutableArray*)members image:(UIImage*)image;
|
||||
|
||||
@interface GroupModel : TSYapDatabaseObject
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *groupMemberIds; //
|
||||
@property (nonatomic, strong) UIImage *groupImage;
|
||||
@property (nonatomic, strong) NSString *groupName;
|
||||
@property (nonatomic, strong) NSData* groupId;
|
||||
|
||||
-(instancetype)initWithTitle:(NSString*)title memberIds:(NSMutableArray*)members image:(UIImage*)image groupId:(NSData*)groupId;
|
||||
|
||||
- (BOOL)isEqual:(id)other;
|
||||
- (BOOL)isEqualToGroupModel:(GroupModel *)model;
|
||||
|
||||
@end
|
||||
|
|
|
@ -10,13 +10,44 @@
|
|||
|
||||
@implementation GroupModel
|
||||
|
||||
-(instancetype)initWithTitle:(NSString*)title members:(NSMutableArray*)members image:(UIImage*)image
|
||||
{
|
||||
-(instancetype)initWithTitle:(NSString*)title memberIds:(NSMutableArray*)memberIds image:(UIImage*)image groupId:(NSData *)groupId{
|
||||
_groupName=title;
|
||||
_groupMembers = [members copy];
|
||||
_groupMemberIds = [memberIds copy];
|
||||
_groupImage = image;
|
||||
_groupId = groupId;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other {
|
||||
if (other == self) {
|
||||
return YES;
|
||||
}
|
||||
if (!other || ![other isKindOfClass:[self class]]) {
|
||||
return NO;
|
||||
}
|
||||
return [self isEqualToGroupModel:other];
|
||||
}
|
||||
|
||||
- (BOOL)isEqualToGroupModel:(GroupModel *)other {
|
||||
if (self == other)
|
||||
return YES;
|
||||
if(![_groupId isEqualToData:other.groupId] ) {
|
||||
return NO;
|
||||
}
|
||||
if (![_groupName isEqual:other.groupName]) {
|
||||
return NO;
|
||||
}
|
||||
if( !(_groupImage!=nil && other.groupImage!=nil && [UIImagePNGRepresentation(_groupImage) isEqualToData:UIImagePNGRepresentation(other.groupImage)])) {
|
||||
return NO;
|
||||
}
|
||||
NSMutableArray* compareMyGroupMemberIds = [NSMutableArray arrayWithArray:_groupMemberIds];
|
||||
[compareMyGroupMemberIds removeObjectsInArray:other.groupMemberIds];
|
||||
if([compareMyGroupMemberIds count] > 0 ) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,12 +8,14 @@
|
|||
|
||||
#import "JSQMessagesViewController.h"
|
||||
#import "JSQMessages.h"
|
||||
|
||||
#import "GroupModel.h"
|
||||
@class TSThread;
|
||||
|
||||
@interface MessagesViewController : JSQMessagesViewController <UIImagePickerControllerDelegate,UINavigationControllerDelegate>
|
||||
|
||||
- (void)setupWithThread:(TSThread*)thread;
|
||||
- (void)setupWithTSIdentifier:(NSString*)identifier;
|
||||
- (void)setupWithTSGroup:(GroupModel*)model;
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#import "MessagesViewController.h"
|
||||
#import "FullImageViewController.h"
|
||||
#import "FingerprintViewController.h"
|
||||
#import "NewGroupViewController.h"
|
||||
|
||||
#import "JSQCallCollectionViewCell.h"
|
||||
#import "JSQCall.h"
|
||||
|
@ -49,6 +50,9 @@
|
|||
#import "ContactsManager.h"
|
||||
|
||||
static NSTimeInterval const kTSMessageSentDateShowTimeInterval = 5 * 60;
|
||||
static NSString *const kUpdateGroupSegueIdentifier = @"updateGroupSegue";
|
||||
static NSString *const kFingerprintSegueIdentifier = @"fingerprintSegue";
|
||||
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
kMediaTypePicture,
|
||||
|
@ -82,16 +86,27 @@ typedef enum : NSUInteger {
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)setupWithTSGroup:(GroupModel*)model {
|
||||
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
self.thread = [TSGroupThread threadWithGroupModel:model transaction:transaction];
|
||||
|
||||
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:self.thread messageBody:@"" attachments:nil];
|
||||
message.groupMetaMessage = TSGroupMessageNew;
|
||||
[[TSMessagesManager sharedManager] sendMessage:message inThread:self.thread];
|
||||
|
||||
isGroupConversation = YES;
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setupWithThread:(TSThread *)thread{
|
||||
self.thread = thread;
|
||||
isGroupConversation = [self.thread isKindOfClass:[TSGroupThread class]];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[self markAllMessagesAsRead];
|
||||
|
||||
isGroupConversation = NO; // TODO: Support Group Conversations
|
||||
|
||||
[self initializeBubbles];
|
||||
|
||||
self.messageMappings = [[YapDatabaseViewMappings alloc] initWithGroups:@[self.thread.uniqueId]
|
||||
|
@ -143,10 +158,10 @@ typedef enum : NSUInteger {
|
|||
|
||||
self.title = self.thread.name;
|
||||
|
||||
UIBarButtonItem * lockButton = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"lock"] style:UIBarButtonItemStylePlain target:self action:@selector(showFingerprint)];
|
||||
|
||||
|
||||
if (!isGroupConversation && [self isRedPhoneReachable]) {
|
||||
|
||||
UIBarButtonItem * lockButton = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"lock"] style:UIBarButtonItemStylePlain target:self action:@selector(showFingerprint)];
|
||||
UIBarButtonItem * callButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"call_tab"] style:UIBarButtonItemStylePlain target:self action:@selector(callAction)];
|
||||
[callButton setImageInsets:UIEdgeInsetsMake(0, -10, 0, -50)];
|
||||
UIBarButtonItem *negativeSeparator = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
|
||||
|
@ -154,7 +169,8 @@ typedef enum : NSUInteger {
|
|||
|
||||
self.navigationItem.rightBarButtonItems = @[negativeSeparator, lockButton, callButton];
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = lockButton;
|
||||
UIBarButtonItem *groupMenuButton = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"settings_tab"] style:UIBarButtonItemStylePlain target:self action:@selector(didPressGroupMenuButton:)];
|
||||
self.navigationItem.rightBarButtonItem = groupMenuButton;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,7 +204,7 @@ typedef enum : NSUInteger {
|
|||
-(void)showFingerprint
|
||||
{
|
||||
[self markAllMessagesAsRead];
|
||||
[self performSegueWithIdentifier:@"fingerprintSegue" sender:self];
|
||||
[self performSegueWithIdentifier:kFingerprintSegueIdentifier sender:self];
|
||||
}
|
||||
|
||||
|
||||
|
@ -363,8 +379,9 @@ typedef enum : NSUInteger {
|
|||
}
|
||||
else {
|
||||
TSMessageAdapter *currentMessage = [self messageAtIndexPath:indexPath];
|
||||
|
||||
TSMessageAdapter *previousMessage = [self messageAtIndexPath:[NSIndexPath indexPathForItem:indexPath.row-1 inSection:indexPath.section]];
|
||||
|
||||
|
||||
NSTimeInterval timeDifference = [currentMessage.date timeIntervalSinceDate:previousMessage.date];
|
||||
if (timeDifference > kTSMessageSentDateShowTimeInterval) {
|
||||
showDate = YES;
|
||||
|
@ -375,10 +392,11 @@ typedef enum : NSUInteger {
|
|||
|
||||
-(NSAttributedString*)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellTopLabelAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TSMessageAdapter * msg = [self messageAtIndexPath:indexPath];
|
||||
if ([self showDateAtIndexPath:indexPath])
|
||||
{
|
||||
return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:msg.date];
|
||||
|
||||
if ([self showDateAtIndexPath:indexPath]) {
|
||||
TSMessageAdapter *currentMessage = [self messageAtIndexPath:indexPath];
|
||||
|
||||
return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:currentMessage.date];
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
@ -387,20 +405,22 @@ typedef enum : NSUInteger {
|
|||
-(BOOL)shouldShowMessageStatusAtIndexPath:(NSIndexPath*)indexPath
|
||||
{
|
||||
|
||||
TSMessageAdapter * currentMessage = [self messageAtIndexPath:indexPath];
|
||||
|
||||
if (indexPath.item == [self.collectionView numberOfItemsInSection:indexPath.section]-1)
|
||||
{
|
||||
return [self isMessageOutgoingAndDelivered:currentMessage];
|
||||
TSMessageAdapter *currentMessage = [self messageAtIndexPath:indexPath];
|
||||
if([self.thread isKindOfClass:[TSGroupThread class]]) {
|
||||
return currentMessage.messageType == TSIncomingMessageAdapter;
|
||||
}
|
||||
|
||||
if (![self isMessageOutgoingAndDelivered:currentMessage])
|
||||
{
|
||||
return NO;
|
||||
else {
|
||||
if (indexPath.item == [self.collectionView numberOfItemsInSection:indexPath.section]-1) {
|
||||
return [self isMessageOutgoingAndDelivered:currentMessage];
|
||||
}
|
||||
|
||||
if (![self isMessageOutgoingAndDelivered:currentMessage]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
TSMessageAdapter *nextMessage = [self nextOutgoingMessage:indexPath];
|
||||
return ![self isMessageOutgoingAndDelivered:nextMessage];
|
||||
}
|
||||
|
||||
TSMessageAdapter * nextMessage = [self nextOutgoingMessage:indexPath];
|
||||
return ![self isMessageOutgoingAndDelivered:nextMessage];
|
||||
}
|
||||
|
||||
-(TSMessageAdapter*)nextOutgoingMessage:(NSIndexPath*)indexPath
|
||||
|
@ -422,19 +442,29 @@ typedef enum : NSUInteger {
|
|||
}
|
||||
|
||||
|
||||
-(NSAttributedString*)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellBottomLabelAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if ([self shouldShowMessageStatusAtIndexPath:indexPath])
|
||||
{
|
||||
_lastDeliveredMessageIndexPath = indexPath;
|
||||
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
|
||||
textAttachment.bounds = CGRectMake(0, 0, 11.0f, 10.0f);
|
||||
NSMutableAttributedString * attrStr = [[NSMutableAttributedString alloc]initWithString:@"Delivered"];
|
||||
[attrStr appendAttributedString:[NSAttributedString attributedStringWithAttachment:textAttachment]];
|
||||
-(NSAttributedString*)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellBottomLabelAtIndexPath:(NSIndexPath *)indexPath {
|
||||
TSMessageAdapter *msg = [self messageAtIndexPath:indexPath];
|
||||
if ([self shouldShowMessageStatusAtIndexPath:indexPath]) {
|
||||
if([self.thread isKindOfClass:[TSGroupThread class]]) {
|
||||
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
|
||||
textAttachment.bounds = CGRectMake(0, 0, 11.0f, 10.0f);
|
||||
NSString *name = [[Environment getCurrent].contactsManager nameStringForPhoneIdentifier:msg.senderId];
|
||||
name = name ? name : msg.senderId;
|
||||
NSMutableAttributedString * attrStr = [[NSMutableAttributedString alloc]initWithString:name];
|
||||
[attrStr appendAttributedString:[NSAttributedString attributedStringWithAttachment:textAttachment]];
|
||||
|
||||
return (NSAttributedString*)attrStr;
|
||||
return (NSAttributedString*)attrStr;
|
||||
}
|
||||
else {
|
||||
_lastDeliveredMessageIndexPath = indexPath;
|
||||
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] init];
|
||||
textAttachment.bounds = CGRectMake(0, 0, 11.0f, 10.0f);
|
||||
NSMutableAttributedString * attrStr = [[NSMutableAttributedString alloc]initWithString:@"Delivered"];
|
||||
[attrStr appendAttributedString:[NSAttributedString attributedStringWithAttachment:textAttachment]];
|
||||
|
||||
return (NSAttributedString*)attrStr;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
@ -442,8 +472,12 @@ typedef enum : NSUInteger {
|
|||
layout:(JSQMessagesCollectionViewFlowLayout *)collectionViewLayout heightForCellBottomLabelAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TSMessageAdapter * msg = [self messageAtIndexPath:indexPath];
|
||||
|
||||
if (msg.messageType == TSOutgoingMessageAdapter) {
|
||||
if([self.thread isKindOfClass:[TSGroupThread class]]) {
|
||||
if(msg.messageType == TSIncomingMessageAdapter) {
|
||||
return 16.0f;
|
||||
}
|
||||
}
|
||||
else if (msg.messageType == TSOutgoingMessageAdapter) {
|
||||
return 16.0f;
|
||||
}
|
||||
|
||||
|
@ -547,12 +581,18 @@ typedef enum : NSUInteger {
|
|||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
|
||||
if ([segue.identifier isEqualToString:@"fingerprintSegue"]){
|
||||
if ([segue.identifier isEqualToString:kFingerprintSegueIdentifier]){
|
||||
FingerprintViewController *vc = [segue destinationViewController];
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
[vc configWithThread:self.thread];
|
||||
}];
|
||||
}
|
||||
else if ([segue.identifier isEqualToString:kUpdateGroupSegueIdentifier]) {
|
||||
NewGroupViewController *vc = [segue destinationViewController];
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
[vc configWithThread:(TSGroupThread*)self.thread];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -652,7 +692,6 @@ typedef enum : NSUInteger {
|
|||
// Process the notification(s),
|
||||
// and get the change-set(s) as applies to my view and mappings configuration.
|
||||
NSArray *notifications = [self.uiDatabaseConnection beginLongLivedReadTransaction];
|
||||
|
||||
NSArray *messageRowChanges = nil;
|
||||
|
||||
[[self.uiDatabaseConnection ext:TSMessageDatabaseViewExtensionName] getSectionChanges:nil
|
||||
|
@ -733,6 +772,44 @@ typedef enum : NSUInteger {
|
|||
TSInteraction *interaction = [self interactionAtIndexPath:indexPath];
|
||||
return [TSMessageAdapter messageViewDataWithInteraction:interaction inThread:self.thread];
|
||||
}
|
||||
#pragma mark group action view
|
||||
-(void)didPressGroupMenuButton:(UIButton *)sender
|
||||
{
|
||||
[self.inputToolbar.contentView.textView resignFirstResponder];
|
||||
|
||||
UIView *presenter = self.parentViewController.view;
|
||||
|
||||
[DJWActionSheet showInView:presenter
|
||||
withTitle:nil
|
||||
cancelButtonTitle:@"Cancel"
|
||||
destructiveButtonTitle:nil
|
||||
otherButtonTitles:@[@"Update group", @"Leave group", @"Delete thread"]
|
||||
tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
|
||||
if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
|
||||
NSLog(@"User Cancelled");
|
||||
} else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) {
|
||||
NSLog(@"Destructive button tapped");
|
||||
}else {
|
||||
switch (tappedButtonIndex) {
|
||||
case 0:
|
||||
DDLogDebug(@"update group picked");
|
||||
[self performSegueWithIdentifier:kUpdateGroupSegueIdentifier sender:self];
|
||||
|
||||
break;
|
||||
case 1:
|
||||
DDLogDebug(@"leave group picket");
|
||||
break;
|
||||
case 2:
|
||||
DDLogDebug(@"delete thread");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark Accessory View
|
||||
|
||||
|
|
|
@ -7,9 +7,11 @@
|
|||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TSGroupThread.h"
|
||||
|
||||
@interface NewGroupViewController : UIViewController <UITableViewDelegate, UITabBarDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate>
|
||||
|
||||
- (void)configWithThread:(TSGroupThread*)thread;
|
||||
@property(nonatomic, strong) IBOutlet UITableView* tableView;
|
||||
|
||||
@property(nonatomic, strong) IBOutlet UITextField* nameGroupTextField;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#import "Contact.h"
|
||||
#import "GroupModel.h"
|
||||
#import "SecurityUtils.h"
|
||||
#import "SignalKeyingStorage.h"
|
||||
|
||||
#import "UIUtil.h"
|
||||
#import "DJWActionSheet.h"
|
||||
|
@ -24,19 +26,29 @@
|
|||
@interface NewGroupViewController () {
|
||||
NSArray* contacts;
|
||||
}
|
||||
@property TSGroupThread* thread;
|
||||
|
||||
@end
|
||||
|
||||
@implementation NewGroupViewController
|
||||
|
||||
- (void)configWithThread:(TSGroupThread *)gThread{
|
||||
_thread = gThread;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Create" style:UIBarButtonItemStylePlain target:self action:@selector(createGroup)];
|
||||
self.navigationItem.title = @"New Group";
|
||||
|
||||
if(_thread==nil) {
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Create" style:UIBarButtonItemStylePlain target:self action:@selector(createGroup)];
|
||||
self.navigationItem.title = @"New Group";
|
||||
|
||||
}
|
||||
else {
|
||||
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Update" style:UIBarButtonItemStylePlain target:self action:@selector(updateGroup)];
|
||||
self.navigationItem.title = _thread.groupModel.groupName;
|
||||
self.nameGroupTextField.text = _thread.groupModel.groupName;
|
||||
}
|
||||
contacts = [Environment getCurrent].contactsManager.textSecureContacts;
|
||||
|
||||
|
||||
[self initializeDelegates];
|
||||
[self initializeTableView];
|
||||
[self initializeKeyboardHandlers];
|
||||
|
@ -73,28 +85,29 @@
|
|||
|
||||
#pragma mark - Actions
|
||||
-(void)createGroup {
|
||||
SignalsViewController* s = (SignalsViewController*)((UINavigationController*)[((UITabBarController*)self.parentViewController.presentingViewController).childViewControllers objectAtIndex:1]).topViewController;
|
||||
|
||||
s.groupFromCompose = [self makeGroup];
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:^(){
|
||||
[s performSegueWithIdentifier:@"showSegue" sender:nil];
|
||||
}];
|
||||
GroupModel* model = [self makeGroup];
|
||||
[Environment groupModel:model];
|
||||
}
|
||||
|
||||
|
||||
-(void)updateGroup {
|
||||
DDLogDebug(@"Update gruop not implemented");
|
||||
}
|
||||
|
||||
|
||||
-(GroupModel*)makeGroup {
|
||||
|
||||
//TODO: Add it to Envirronment
|
||||
|
||||
NSString* title = _nameGroupTextField.text;
|
||||
UIImage* img = _groupImageButton.imageView.image;
|
||||
NSMutableArray* mut = [[NSMutableArray alloc]init];
|
||||
|
||||
for (NSIndexPath* idx in _tableView.indexPathsForSelectedRows) {
|
||||
[mut addObject:[contacts objectAtIndex:(NSUInteger)idx.row-1]];
|
||||
[mut addObjectsFromArray:[[contacts objectAtIndex:(NSUInteger)idx.row-1] textSecureIdentifiers]];
|
||||
}
|
||||
// Also add the originator
|
||||
[mut addObject:[SignalKeyingStorage.localNumber toE164]];
|
||||
NSData* groupId = [SecurityUtils generateRandomBytes:16];
|
||||
|
||||
return [[GroupModel alloc] initWithTitle:title members:mut image:img];
|
||||
return [[GroupModel alloc] initWithTitle:title memberIds:mut image:img groupId:groupId];
|
||||
}
|
||||
|
||||
-(IBAction)addGroupPhoto:(id)sender
|
||||
|
@ -198,12 +211,17 @@
|
|||
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: indexPath.row == 0 ? @"HeaderCell" : @"GroupSearchCell"];
|
||||
}
|
||||
|
||||
if (indexPath.row > 0) {
|
||||
NSUInteger row = (NSUInteger)indexPath.row;
|
||||
Contact* contact = contacts[row-1];
|
||||
|
||||
cell.textLabel.attributedText = [self attributedStringForContact:contact inCell:cell];
|
||||
NSUInteger row = (NSUInteger)indexPath.row;
|
||||
Contact* contact = contacts[row-1];
|
||||
if(_thread) {
|
||||
//TODOGROUP inefficient way of doing this, will not scale well
|
||||
NSMutableSet *usersInGroup = [NSMutableSet setWithArray:_thread.groupModel.groupMemberIds];
|
||||
[usersInGroup intersectSet:[NSSet setWithArray:contact.userTextPhoneNumbers]];
|
||||
cell.accessoryType = [usersInGroup count]>0 ? UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
|
||||
}
|
||||
|
||||
cell.textLabel.attributedText = [self attributedStringForContact:contact inCell:cell];
|
||||
|
||||
} else {
|
||||
cell.textLabel.text = @"Add People:";
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
@property (nonatomic) NSString *contactIdentifierFromCompose;
|
||||
@property (nonatomic) GroupModel *groupFromCompose;
|
||||
|
||||
@property (nonatomic, retain) IBOutlet UITableView *tableView;
|
||||
@property (nonatomic, retain) IBOutlet UISegmentedControl *inboxArchiveSwitch;
|
||||
|
||||
|
|
|
@ -161,12 +161,20 @@ static NSString *const kSegueIndentifier = @"showSegue";
|
|||
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
|
||||
TSThread *thread = [self threadForIndexPath:selectedIndexPath];
|
||||
|
||||
if (thread) {
|
||||
[vc setupWithThread:thread];
|
||||
} else if (self.contactIdentifierFromCompose){
|
||||
if (self.contactIdentifierFromCompose){
|
||||
[vc setupWithTSIdentifier:self.contactIdentifierFromCompose];
|
||||
self.contactIdentifierFromCompose = nil;
|
||||
}
|
||||
else if (self.groupFromCompose) {
|
||||
[vc setupWithTSGroup:self.groupFromCompose];
|
||||
self.groupFromCompose = nil;
|
||||
}
|
||||
else if (thread) {
|
||||
[vc setupWithThread:thread];
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,4 +108,6 @@
|
|||
}];
|
||||
}
|
||||
|
||||
// TODO: group storage tests
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in a new issue