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:
Christine Corbett 2014-12-16 21:44:36 -08:00 committed by Frederic Jacobs
parent c74899661c
commit 333c920e0b
27 changed files with 405 additions and 292 deletions

View File

@ -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 */,

View File

@ -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>

View File

@ -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

View File

@ -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];

View File

@ -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

View File

@ -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

View File

@ -11,7 +11,6 @@
#import "ContactsManager.h"
#import "TSInteraction.h"
#import "TSStorageManager.h"
#import "TSGroup.h"
#import "TSCall.h"
#import "TSOutgoingMessage.h"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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];

View File

@ -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{

View File

@ -18,5 +18,4 @@ typedef NS_ENUM(NSInteger, TSOutgoingMessageState){
};
@property (nonatomic) TSOutgoingMessageState messageState;
@end

View File

@ -20,6 +20,12 @@
if (self) {
_messageState = TSOutgoingMessageStateAttemptingOut;
if( [thread isKindOfClass:[TSGroupThread class]]) {
self.groupMetaMessage = TSGroupMessageDeliver;
}
else {
self.groupMetaMessage = TSGroupMessageNone;
}
}
return self;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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:";

View File

@ -16,7 +16,6 @@
@property (nonatomic) NSString *contactIdentifierFromCompose;
@property (nonatomic) GroupModel *groupFromCompose;
@property (nonatomic, retain) IBOutlet UITableView *tableView;
@property (nonatomic, retain) IBOutlet UISegmentedControl *inboxArchiveSwitch;

View File

@ -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];
}
}
}

View File

@ -108,4 +108,6 @@
}];
}
// TODO: group storage tests
@end