Groups: Update messages, avatars and bug fixes

pull/1/head
Christine Corbett 10 years ago committed by Frederic Jacobs
parent 3c568f7044
commit e58f9bf966

@ -284,6 +284,7 @@
A1C32D5117A06544000A904E /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C32D4D17A0652C000A904E /* AddressBook.framework */; };
A56977911A351BC400173BF2 /* ScanIdentityBarcodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A569778E1A351BC400173BF2 /* ScanIdentityBarcodeViewController.m */; };
A56977921A351BC400173BF2 /* PresentIdentityQRCodeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A569778F1A351BC400173BF2 /* PresentIdentityQRCodeViewController.m */; };
A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */; };
AA0C8E498E2046B0B81EEE6E /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8313AE91B4954215858A5662 /* libPods.a */; };
B6019E971A2492AB001118DF /* NSDate+millisecondTimeStamp.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6019E961A2492AB001118DF /* NSDate+millisecondTimeStamp.mm */; };
B60C16651988999D00E97A6C /* VersionMigrations.m in Sources */ = {isa = PBXBuildFile; fileRef = B60C16641988999D00E97A6C /* VersionMigrations.m */; };
@ -882,6 +883,8 @@
A569778E1A351BC400173BF2 /* ScanIdentityBarcodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScanIdentityBarcodeViewController.m; sourceTree = "<group>"; };
A569778F1A351BC400173BF2 /* PresentIdentityQRCodeViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PresentIdentityQRCodeViewController.m; sourceTree = "<group>"; };
A56977901A351BC400173BF2 /* PresentIdentityQRCodeViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PresentIdentityQRCodeViewController.h; sourceTree = "<group>"; };
A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ShowGroupMembersViewController.m; sourceTree = "<group>"; };
A5D0699A1A50E9CB004CB540 /* ShowGroupMembersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShowGroupMembersViewController.h; sourceTree = "<group>"; };
B6019E951A2492AB001118DF /* NSDate+millisecondTimeStamp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDate+millisecondTimeStamp.h"; sourceTree = "<group>"; };
B6019E961A2492AB001118DF /* NSDate+millisecondTimeStamp.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSDate+millisecondTimeStamp.mm"; sourceTree = "<group>"; };
B60C16631988999D00E97A6C /* VersionMigrations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VersionMigrations.h; sourceTree = "<group>"; };
@ -2683,6 +2686,8 @@
FC31962C1A06A2190094C78E /* FingerprintViewController.m */,
FCB11D911A12A4AA002F93FB /* FullImageViewController.h */,
FCB11D921A12A4AA002F93FB /* FullImageViewController.m */,
A5D0699A1A50E9CB004CB540 /* ShowGroupMembersViewController.h */,
A5D069991A50E9CB004CB540 /* ShowGroupMembersViewController.m */,
FCFD256D1A151BCB00F4C644 /* NewGroupViewController.h */,
FCFD256E1A151BCB00F4C644 /* NewGroupViewController.m */,
FC4FA0241A1B9DC600DA100A /* SignalsNavigationController.h */,
@ -3194,6 +3199,7 @@
FC1615181A37935600F1761D /* NotificationPreviewViewController.m in Sources */,
E197B60D18BBEC1A00F073E5 /* AudioSocket.m in Sources */,
FCF72A081A01A765006BC849 /* ContactsTableViewController.m in Sources */,
A5D0699B1A50E9CB004CB540 /* ShowGroupMembersViewController.m in Sources */,
FC31962D1A06A2190094C78E /* FingerprintViewController.m in Sources */,
76EB061418170B33006006FC /* AnonymousConditionLogger.m in Sources */,
76EB05C018170B33006006FC /* DhPacket.m in Sources */,
@ -3558,7 +3564,7 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_OPTIMIZATION_LEVEL = 3;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@ -3729,7 +3735,7 @@
GCC_DYNAMIC_NO_PIC = NO;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_OPTIMIZATION_LEVEL = 3;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
@ -3796,7 +3802,7 @@
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_GENERATE_TEST_COVERAGE_FILES = NO;
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_OPTIMIZATION_LEVEL = 3;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14C68k" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14B25" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
@ -121,6 +121,7 @@
<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"/>
<segue destination="JeZ-9g-U61" kind="show" identifier="showGroupMembersSegue" id="Gc6-AD-JV1"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="yXZ-iE-5va" userLabel="First Responder" sceneMemberID="firstResponder"/>
@ -798,7 +799,32 @@ A0 09 9A FF A8 8A 09 99</string>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="sd1-mY-rAe" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4645.5" y="-261"/>
<point key="canvasLocation" x="4597.5" y="-276"/>
</scene>
<!--Show Group Members View Controller-->
<scene sceneID="VBt-Ax-0G9">
<objects>
<tableViewController id="JeZ-9g-U61" customClass="ShowGroupMembersViewController" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="Fdx-Zk-e27">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="hyn-Ss-OAa">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hyn-Ss-OAa" id="4XE-JO-Upr">
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="JeZ-9g-U61" id="xVC-pb-xNg"/>
<outlet property="delegate" destination="JeZ-9g-U61" id="qYP-nN-CLJ"/>
</connections>
</tableView>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="3R8-C6-Zq8" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4561.5" y="452.25"/>
</scene>
<!--Present IdentityQR Code View Controller-->
<scene sceneID="b0A-fL-uiD">
@ -2530,7 +2556,7 @@ A0 09 9A FF A8 8A 09 99</string>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="srg-3q-gF9" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3154.5" y="392.25"/>
<point key="canvasLocation" x="3172.5" y="437.25"/>
</scene>
<!--Signals Navigation Controller-->
<scene sceneID="miN-Ma-3eR">
@ -3707,11 +3733,13 @@ Licensed under the GPLv3</string>
<outlet property="nameGroupTextField" destination="gbm-B5-gCc" id="XC8-Ci-sl2"/>
<outlet property="tableView" destination="cFo-AT-Srf" id="NqG-W1-Vhy"/>
<outlet property="tapToDismissView" destination="Ukg-om-VX3" id="nyq-IV-JDV"/>
<segue destination="JOx-5h-y9g" kind="unwind" identifier="UnwindToMessagesViewSegue" unwindAction="unwindGroupUpdated:" id="IGD-29-LA4"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="GsM-dR-L7j" userLabel="First Responder" sceneMemberID="firstResponder"/>
<exit id="JOx-5h-y9g" userLabel="Exit" sceneMemberID="exit"/>
</objects>
<point key="canvasLocation" x="4197" y="-899.25"/>
<point key="canvasLocation" x="4545" y="-974.25"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="kfT-eG-hkf">

@ -43,6 +43,7 @@ phoneDirectoryManager;
require(environment != nil);
return environment;
}
+(void) setCurrent:(Environment*)curEnvironment {
environment = curEnvironment;
}
@ -162,7 +163,6 @@ phoneDirectoryManager;
return [PropertyListPreferences new];
}
- (void)setSignalsViewController:(SignalsViewController *)signalsViewController{
_signalsViewController = signalsViewController;
}
@ -208,7 +208,6 @@ phoneDirectoryManager;
}
+ (void)resetAppData{
[SignalKeyingStorage wipeKeychain];
[Environment.preferences clear];

@ -13,7 +13,7 @@
@interface TSContactThread : TSThread
+ (instancetype)threadWithContactId:(NSString*)contactId transaction:(YapDatabaseReadWriteTransaction*)transaction;
+ (instancetype)getOrCreateThreadWithContactId:(NSString*)contactId transaction:(YapDatabaseReadWriteTransaction*)transaction;
- (NSString*)contactIdentifier;
- (TSRecipient *)recipientWithTransaction:(YapDatabaseReadTransaction*)transaction;

@ -26,7 +26,7 @@
return self;
}
+ (instancetype)threadWithContactId:(NSString*)contactId transaction:(YapDatabaseReadWriteTransaction*)transaction {
+ (instancetype)getOrCreateThreadWithContactId:(NSString*)contactId transaction:(YapDatabaseReadWriteTransaction*)transaction {
TSContactThread *thread = [self fetchObjectWithUniqueID:[self threadIdFromContactId:contactId] transaction:transaction];

@ -11,8 +11,9 @@
@interface TSGroupThread : TSThread
@property (nonatomic,strong) GroupModel* groupModel;
+ (instancetype)threadWithGroupModel:(GroupModel *)groupModel transaction:(YapDatabaseReadWriteTransaction*)transaction;
+ (instancetype)getOrCreateThreadWithGroupModel:(GroupModel *)groupModel transaction:(YapDatabaseReadWriteTransaction*)transaction;
+ (instancetype)threadWithGroupModel:(GroupModel *)groupModel transaction:(YapDatabaseReadTransaction*)transaction;
- (NSData*)groupId;
- (NSArray *)recipientsWithTransaction:(YapDatabaseReadTransaction*)transaction;

@ -24,17 +24,17 @@
}
+ (instancetype)threadWithGroupModel:(GroupModel *)groupModel transaction:(YapDatabaseReadWriteTransaction*)transaction{
+ (instancetype)threadWithGroupModel:(GroupModel *)groupModel transaction:(YapDatabaseReadTransaction*)transaction {
return [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupModel.groupId] transaction:transaction];
}
+ (instancetype)getOrCreateThreadWithGroupModel:(GroupModel *)groupModel transaction:(YapDatabaseReadWriteTransaction*)transaction{
TSGroupThread *thread = [self fetchObjectWithUniqueID:[self threadIdFromGroupId:groupModel.groupId] transaction:transaction];
if (!thread) {
thread = [[TSGroupThread alloc] initWithGroupModel:groupModel];
[thread saveWithTransaction:transaction];
}
else if(![thread.groupModel isEqual:groupModel]) {
thread.groupModel = groupModel;
[thread saveWithTransaction:transaction];
}
return thread;
}

@ -15,8 +15,14 @@
- (instancetype)initWithIdentifier:(uint64_t)identifier
key:(NSData*)key
contentType:(NSString*)contentType
relay:(NSString*)relay NS_DESIGNATED_INITIALIZER;;
relay:(NSString*)relay NS_DESIGNATED_INITIALIZER;
@property NSString *relay;
- (instancetype)initWithIdentifier:(uint64_t)identifier
key:(NSData*)key
contentType:(NSString*)contentType
relay:(NSString*)relay
avatarOfGroupId:(NSData*)avatarOfGroupId;
@property NSString *relay;
@property NSData *avatarOfGroupId;
@end

@ -24,6 +24,26 @@
return self;
}
- (instancetype)initWithIdentifier:(uint64_t)identifier
key:(NSData*)key
contentType:(NSString*)contentType
relay:(NSString*)relay
avatarOfGroupId:(NSData *)avatarOfGroupId {
self = [self initWithIdentifier:identifier
key:key
contentType:contentType
relay:relay];
if(self){
_relay = relay;
_avatarOfGroupId = avatarOfGroupId;
}
return self;
}
- (BOOL)isDownloaded{
return NO;
}

@ -22,4 +22,5 @@
encryptionKey:(NSData*)encryptionKey
contentType:(NSString*)contentType;
@end

@ -24,6 +24,8 @@
return self;
}
+ (NSString *)collection{
return @"TSAttachements";
}

@ -41,7 +41,7 @@
}
- (instancetype)initWithSignal:(IncomingPushMessageSignal*)signal transaction:(YapDatabaseReadWriteTransaction*)transaction failedMessageType:(TSErrorMessageType)errorMessageType{
TSContactThread *contactThread = [TSContactThread threadWithContactId:signal.source transaction:transaction];
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:signal.source transaction:transaction];
return [self initWithTimestamp:signal.timestamp inThread:contactThread failedMessageType:errorMessageType];
}
@ -84,7 +84,7 @@
}
+ (instancetype)untrustedKeyWithSignal:(IncomingPushMessageSignal*)preKeyMessage withTransaction:(YapDatabaseReadWriteTransaction*)transaction{
TSContactThread *contactThread = [TSContactThread threadWithContactId:preKeyMessage.source transaction:transaction];
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:preKeyMessage.source transaction:transaction];
TSErrorMessage *errorMessage = [[self alloc] initForUnknownIdentityKeyWithTimestamp:preKeyMessage.timestamp inThread:contactThread incomingPushSignal:preKeyMessage.data];
return errorMessage;
}

@ -14,13 +14,16 @@ typedef NS_ENUM(NSInteger, TSInfoMessageType){
TSInfoMessageTypeSessionDidEnd,
TSInfoMessageUserNotRegistered,
TSInfoMessageTypeUnsupportedMessage,
TSInfoMessageTypeGroupUpdate
TSInfoMessageTypeGroupUpdate,
TSInfoMessageTypeGroupQuit
};
+ (instancetype)userNotRegisteredMessageInThread:(TSThread*)thread transaction:(YapDatabaseReadWriteTransaction*)transaction;
@property TSInfoMessageType messageType;
@property NSString* customMessage;
- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)contact messageType:(TSInfoMessageType)infoMessage;
- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread messageType:(TSInfoMessageType)infoMessage customMessage:(NSString*) customMessage;
@end

@ -21,6 +21,14 @@
return self;
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread messageType:(TSInfoMessageType)infoMessage customMessage:(NSString*)customMessage {
self = [self initWithTimestamp:timestamp inThread:thread messageType:infoMessage];
if (self) {
_customMessage = customMessage;
}
return self;
}
+ (instancetype)userNotRegisteredMessageInThread:(TSThread*)thread transaction:(YapDatabaseReadWriteTransaction*)transaction{
return [[self alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageType:TSInfoMessageUserNotRegistered];
@ -34,8 +42,10 @@
return @"Media messages are currently not supported.";
case TSInfoMessageUserNotRegistered:
return @"The user is not registered.";
case TSInfoMessageTypeGroupQuit:
return @"You have left the group.";
case TSInfoMessageTypeGroupUpdate:
return @"Updated the group";
return _customMessage != nil ? _customMessage : @"Updated the group";
default:
break;
}

@ -82,7 +82,7 @@ const struct TSMessageEdges TSMessageEdges = {
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction{
[super saveWithTransaction:transaction];
TSThread *fetchedThread = [TSThread fetchObjectWithUniqueID:self.uniqueThreadId];
TSThread *fetchedThread = [TSThread fetchObjectWithUniqueID:self.uniqueThreadId transaction:transaction];
uint64_t timeStamp = [TSInteraction timeStampFromString:self.uniqueId];
if (timeStamp > fetchedThread.lastMessageId) {

@ -15,4 +15,6 @@
- (void)retrieveAttachment:(TSAttachment*)attachment;
- (void)sendAttachment:(NSData*)attachmentData contentType:(NSString*)contentType thread:(TSThread*)thread;
- (void)sendAttachment:(NSData*)attachmentData contentType:(NSString*)contentType inMessage:(TSOutgoingMessage*)outgoingMessage thread:(TSThread*)thread;
@end

@ -40,25 +40,25 @@ dispatch_queue_t attachmentsQueue() {
@implementation TSMessagesManager (attachments)
- (void)handleReceivedMediaMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content {
NSMutableArray *attachments = [NSMutableArray array];
NSArray *attachmentsToRetrieve = (content.group != nil && (content.group.type == PushMessageContentGroupContextTypeUpdate)) ? [NSArray arrayWithObject:content.group.avatar] : content.attachments;
NSMutableArray *retrievedAttachments = [NSMutableArray array];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
for (PushMessageContentAttachmentPointer *pointer in content.attachments) {
TSAttachmentPointer *attachmentPointer = [[TSAttachmentPointer alloc] initWithIdentifier:pointer.id key:pointer.key contentType:pointer.contentType relay:message.relay];
for (PushMessageContentAttachmentPointer *pointer in attachmentsToRetrieve) {
TSAttachmentPointer *attachmentPointer = (content.group != nil && (content.group.type == PushMessageContentGroupContextTypeUpdate)) ? [[TSAttachmentPointer alloc] initWithIdentifier:pointer.id key:pointer.key contentType:pointer.contentType relay:message.relay avatarOfGroupId:content.group.id] : [[TSAttachmentPointer alloc] initWithIdentifier:pointer.id key:pointer.key contentType:pointer.contentType relay:message.relay];
[attachmentPointer saveWithTransaction:transaction];
dispatch_async(attachmentsQueue(), ^{
[self retrieveAttachment:attachmentPointer];
});
[attachments addObject:attachmentPointer.uniqueId];
[retrievedAttachments addObject:attachmentPointer.uniqueId];
}
}];
[self handleReceivedMessage:message withContent:content attachments:attachments];
[self handleReceivedMessage:message withContent:content attachments:retrievedAttachments];
}
- (void)sendAttachment:(NSData*)attachmentData contentType:(NSString*)contentType thread:(TSThread*)thread {
- (void)sendAttachment:(NSData*)attachmentData contentType:(NSString*)contentType inMessage:(TSOutgoingMessage*)outgoingMessage thread:(TSThread*)thread {
TSRequest *allocateAttachment = [[TSAllocAttachmentRequest alloc] init];
[[TSNetworkManager sharedManager] queueAuthenticatedRequest:allocateAttachment success:^(NSURLSessionDataTask *task, id responseObject) {
dispatch_async(attachmentsQueue(), ^{
@ -76,8 +76,8 @@ dispatch_queue_t attachmentsQueue() {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[result.pointer saveWithTransaction:transaction];
}];
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil attachments:@[attachementId]];
[self sendMessage:message inThread:thread];
[outgoingMessage.attachments addObject:attachementId];
[self sendMessage:outgoingMessage inThread:thread];
} else{
DDLogWarn(@"Failed to upload attachment");
}
@ -87,9 +87,14 @@ dispatch_queue_t attachmentsQueue() {
});
} failure:^(NSURLSessionDataTask *task, NSError *error) {
DDLogError(@"Failed to get attachment allocated: %@", error);
}];
}];
}
- (void)sendAttachment:(NSData*)attachmentData contentType:(NSString*)contentType thread:(TSThread*)thread {
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:nil attachments:[[NSMutableArray alloc] init]];
[self sendAttachment:attachmentData contentType:contentType inMessage:message thread:thread];
}
- (void)retrieveAttachment:(TSAttachmentPointer*)attachment {
TSAttachmentRequest *attachmentRequest = [[TSAttachmentRequest alloc] initWithId:[attachment identifier]
@ -122,6 +127,13 @@ dispatch_queue_t attachmentsQueue() {
contentType:attachment.contentType];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[stream saveWithTransaction:transaction];
if([attachment.avatarOfGroupId length]!=0) {
GroupModel *emptyModelToFillOutId = [[GroupModel alloc] initWithTitle:nil memberIds:nil image:nil groupId:attachment.avatarOfGroupId]; // TODO refactor the TSGroupThread to just take in an ID (as it is all that it uses). Should not take in more than it uses
TSGroupThread* gThread = [TSGroupThread getOrCreateThreadWithGroupModel:emptyModelToFillOutId transaction:transaction];
gThread.groupModel.groupImage=[stream image];
[gThread saveWithTransaction:transaction];
}
}];
}
}

@ -65,7 +65,7 @@ dispatch_queue_t sendingQueue() {
}];
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.
// we don't need to send the message to ourselves, but otherwise we sends
if( ![[rec uniqueId] isEqualToString:[SignalKeyingStorage.localNumber toE164]]){
[self sendMessage:message
toRecipient:rec
@ -241,6 +241,12 @@ dispatch_queue_t sendingQueue() {
if(message.groupMetaMessage==TSGroupMessageDeliver) {
[self saveMessage:message withState:message.messageState];
}
else if(message.groupMetaMessage==TSGroupMessageQuit) {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[[TSInfoMessage alloc] initWithTimestamp:message.timeStamp inThread:thread messageType:TSInfoMessageTypeGroupQuit] saveWithTransaction:transaction];
}];
}
else {
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
@ -252,48 +258,61 @@ dispatch_queue_t sendingQueue() {
- (NSData*)plainTextForMessage:(TSOutgoingMessage*)message inThread:(TSThread*)thread{
PushMessageContentBuilder *builder = [PushMessageContentBuilder new];
[builder setBody:message.body];
BOOL processAttachments = YES;
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:
case TSGroupMessageNew: {
if(gThread.groupModel.groupImage!=nil && [message.attachments count] == 1) {
id dbObject = [TSAttachmentStream fetchObjectWithUniqueID:[message.attachments firstObject]];
if ([dbObject isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attachment = (TSAttachmentStream*)dbObject;
PushMessageContentAttachmentPointerBuilder *attachmentbuilder = [PushMessageContentAttachmentPointerBuilder new];
[attachmentbuilder setId:[attachment.identifier unsignedLongLongValue]];
[attachmentbuilder setContentType:attachment.contentType];
[attachmentbuilder setKey:attachment.encryptionKey];
[groupBuilder setAvatar:[attachmentbuilder build]];
processAttachments = NO;
}
}
[groupBuilder setType:PushMessageContentGroupContextTypeUpdate];
break;
}
default:
[groupBuilder setType:PushMessageContentGroupContextTypeDeliver];
break;
}
//[groupBuilder setAvatar:(PushMessageContentAttachmentPointer *)]; //TODOATTACHMENTS for avatar
[groupBuilder setId:gThread.groupModel.groupId];
if(message.groupMetaMessage!=TSGroupMessageQuit) {
[groupBuilder setMembersArray:gThread.groupModel.groupMemberIds];
[groupBuilder setName:gThread.groupModel.groupName];
}
[builder setGroup:groupBuilder.build];
}
NSMutableArray *attachmentsArray = [NSMutableArray array];
for (NSString *attachmentId in message.attachments){
id dbObject = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
if ([dbObject isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attachment = (TSAttachmentStream*)dbObject;
if(processAttachments) {
NSMutableArray *attachmentsArray = [NSMutableArray array];
for (NSString *attachmentId in message.attachments){
id dbObject = [TSAttachmentStream fetchObjectWithUniqueID:attachmentId];
PushMessageContentAttachmentPointerBuilder *attachmentbuilder = [PushMessageContentAttachmentPointerBuilder new];
[attachmentbuilder setId:[attachment.identifier unsignedLongLongValue]];
[attachmentbuilder setContentType:attachment.contentType];
[attachmentbuilder setKey:attachment.encryptionKey];
[attachmentsArray addObject:[attachmentbuilder build]];
if ([dbObject isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attachment = (TSAttachmentStream*)dbObject;
PushMessageContentAttachmentPointerBuilder *attachmentbuilder = [PushMessageContentAttachmentPointerBuilder new];
[attachmentbuilder setId:[attachment.identifier unsignedLongLongValue]];
[attachmentbuilder setContentType:attachment.contentType];
[attachmentbuilder setKey:attachment.encryptionKey];
[attachmentsArray addObject:[attachmentbuilder build]];
}
}
[builder setAttachmentsArray:attachmentsArray];
}
[builder setAttachmentsArray:attachmentsArray];
return [builder.build data];
}

@ -29,6 +29,7 @@
#import "TSNetworkManager.h"
#import "TSSubmitMessageRequest.h"
#import "TSMessagesManager+attachments.h"
#import "TSAttachmentPointer.h"
#import "NSData+messagePadding.h"
@ -178,13 +179,30 @@
}
- (void)handleIncomingMessage:(IncomingPushMessageSignal*)incomingMessage withPushContent:(PushMessageContent*)content{
if(content.group!= nil ) {
__block BOOL ignoreMessage = NO;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
GroupModel *emptyModelToFillOutId = [[GroupModel alloc] initWithTitle:nil memberIds:nil image:nil groupId:content.group.id]; // TODO refactor the TSGroupThread to just take in an ID (as it is all that it uses). Should not take in more than it uses
TSGroupThread *gThread = [TSGroupThread threadWithGroupModel:emptyModelToFillOutId transaction:transaction];
if(gThread==nil) {
ignoreMessage = YES;
}
}];
if(ignoreMessage) {
DDLogDebug(@"recevied message from group that I left, ignoring");
return;
}
}
if ((content.flags & PushMessageContentFlagsEndSession) != 0) {
DDLogVerbose(@"Received end session message...");
[self handleEndSessionMessage:incomingMessage withContent:content];
} else if (content.attachments.count > 0) {
DDLogVerbose(@"Received push media message (attachment) ...");
}
else if (content.attachments.count > 0 || (content.group!= nil && content.group.type == PushMessageContentGroupContextTypeUpdate && content.group.hasAvatar)) {
DDLogVerbose(@"Received push media message (attachment) or group with an avatar...");
[self handleReceivedMediaMessage:incomingMessage withContent:content];
} else {
}
else {
DDLogVerbose(@"Received individual push text message...");
[self handleReceivedTextMessage:incomingMessage withContent:content];
}
@ -192,7 +210,7 @@
- (void)handleEndSessionMessage:(IncomingPushMessageSignal*)message withContent:(PushMessageContent*)content{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSContactThread *thread = [TSContactThread threadWithContactId:message.source transaction:transaction];
TSContactThread *thread = [TSContactThread getOrCreateThreadWithContactId:message.source transaction:transaction];
uint64_t timeStamp = message.timestamp;
if (thread){
@ -216,11 +234,33 @@
TSIncomingMessage *incomingMessage;
TSThread *thread;
if (groupId) {
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];
GroupModel *model = [[GroupModel alloc] initWithTitle:content.group.name memberIds:[[NSMutableArray alloc ] initWithArray:content.group.members] image:nil groupId:content.group.id];
TSGroupThread *gThread = [TSGroupThread getOrCreateThreadWithGroupModel:model transaction:transaction];
[gThread saveWithTransaction:transaction];
if(content.group.type==PushMessageContentGroupContextTypeUpdate) {
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp inThread:gThread messageType:TSInfoMessageTypeGroupUpdate] saveWithTransaction:transaction];
if([attachments count]==1) {
NSString* avatarId = [attachments firstObject];
TSAttachment *avatar = [TSAttachment fetchObjectWithUniqueID:avatarId];
if ([avatar isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *stream = (TSAttachmentStream*)avatar;
if ([stream isImage]) {
model.groupImage = [stream image];
}
}
}
NSString* updateGroupInfo = [gThread.groupModel getInfoStringAboutUpdateTo:model];
gThread.groupModel = model;
[gThread saveWithTransaction:transaction];
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp inThread:gThread messageType:TSInfoMessageTypeGroupUpdate customMessage:updateGroupInfo] saveWithTransaction:transaction];
}
else if(content.group.type==PushMessageContentGroupContextTypeQuit) {
NSString* updateGroupInfo = [NSString stringWithFormat:@"%@ has left group",message.source];
NSMutableArray *newGroupMembers = [NSMutableArray arrayWithArray:gThread.groupModel.groupMemberIds];
[newGroupMembers removeObject:message.source];
gThread.groupModel.groupMemberIds = newGroupMembers;
[gThread saveWithTransaction:transaction];
[[[TSInfoMessage alloc] initWithTimestamp:timeStamp inThread:gThread messageType:TSInfoMessageTypeGroupUpdate customMessage:updateGroupInfo] saveWithTransaction:transaction];
}
else {
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp inThread:gThread authorId:message.source messageBody:body attachments:attachments];
@ -229,7 +269,7 @@
thread = gThread;
}
else{
TSContactThread *cThread = [TSContactThread threadWithContactId:message.source transaction:transaction];
TSContactThread *cThread = [TSContactThread getOrCreateThreadWithContactId:message.source transaction:transaction];
[cThread saveWithTransaction:transaction];
incomingMessage = [[TSIncomingMessage alloc] initWithTimestamp:timeStamp inThread:cThread messageBody:body attachments:attachments];
[incomingMessage saveWithTransaction:transaction];

@ -14,7 +14,7 @@
if (self.imageOrientation == UIImageOrientationUp) return self;
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
[self drawInRect:(CGRect){0, 0, self.size}];
[self drawInRect:(CGRect){{0, 0}, self.size}];
UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage;

@ -13,7 +13,7 @@
@interface GroupModel : TSYapDatabaseObject
@property (nonatomic, strong) NSMutableArray *groupMemberIds; //
@property (nonatomic, strong) NSMutableArray *groupMemberIds;
@property (nonatomic, strong) UIImage *groupImage;
@property (nonatomic, strong) NSString *groupName;
@property (nonatomic, strong) NSData* groupId;
@ -22,5 +22,6 @@
- (BOOL)isEqual:(id)other;
- (BOOL)isEqualToGroupModel:(GroupModel *)model;
- (NSString*) getInfoStringAboutUpdateTo:(GroupModel*)model;
@end

@ -49,5 +49,24 @@
return YES;
}
- (NSString*) getInfoStringAboutUpdateTo:(GroupModel*)newModel {
NSString* updatedGroupInfoString = @"Group updated. ";
if (self == newModel) {
return updatedGroupInfoString;
}
if (![_groupName isEqual:newModel.groupName]) {
updatedGroupInfoString = [updatedGroupInfoString stringByAppendingString:@"Name changed. "];
}
if(_groupImage!=nil && newModel.groupImage!=nil && !([UIImagePNGRepresentation(_groupImage) isEqualToData:UIImagePNGRepresentation(newModel.groupImage)])) {
updatedGroupInfoString = [updatedGroupInfoString stringByAppendingString:@"Avatar changed. "];
}
NSMutableArray* compareMyGroupMemberIds = [NSMutableArray arrayWithArray:_groupMemberIds];
[compareMyGroupMemberIds removeObjectsInArray:newModel.groupMemberIds];
if([compareMyGroupMemberIds count] > 0 ) {
updatedGroupInfoString = [updatedGroupInfoString stringByAppendingString:@"Members changed. "];
}
return updatedGroupInfoString;
}
@end

@ -9,6 +9,7 @@
#import "InboxTableViewCell.h"
#import "Util.h"
#import "UIImage+JSQMessages.h"
#import "TSGroupThread.h"
#define ARCHIVE_IMAGE_VIEW_WIDTH 22.0f
#define DELETE_IMAGE_VIEW_WIDTH 19.0f
@ -46,7 +47,7 @@
-(void)configureWithThread:(TSThread*)thread {
_nameLabel.text = thread.name;
_snippetLabel.text = thread.lastMessageLabel;
_contactPictureView.image = thread.image;
_contactPictureView.image = [thread isKindOfClass:[TSGroupThread class]] ? ((TSGroupThread*)thread).groupModel.groupImage : thread.image;
_timeLabel.attributedText = [self dateAttributedString:thread.lastMessageDate];
self.separatorInset = UIEdgeInsetsMake(0,_contactPictureView.frame.size.width*1.5f, 0, 0);

@ -12,6 +12,9 @@
#import "FullImageViewController.h"
#import "FingerprintViewController.h"
#import "NewGroupViewController.h"
#import "ShowGroupMembersViewController.h"
#import "SignalKeyingStorage.h"
#import "JSQCallCollectionViewCell.h"
#import "JSQCall.h"
@ -51,8 +54,9 @@
#import "PreferencesUtil.h"
static NSTimeInterval const kTSMessageSentDateShowTimeInterval = 5 * 60;
static NSString *const kUpdateGroupSegueIdentifier = @"updateGroupSegue";
static NSString *const kFingerprintSegueIdentifier = @"fingerprintSegue";
static NSString *const kUpdateGroupSegueIdentifier = @"updateGroupSegue";
static NSString *const kFingerprintSegueIdentifier = @"fingerprintSegue";
static NSString *const kShowGroupMembersSegue = @"showGroupMembersSegue";
typedef enum : NSUInteger {
@ -83,18 +87,22 @@ typedef enum : NSUInteger {
- (void)setupWithTSIdentifier:(NSString *)identifier{
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
self.thread = [TSContactThread threadWithContactId:identifier transaction:transaction];
self.thread = [TSContactThread getOrCreateThreadWithContactId:identifier transaction:transaction];
}];
}
- (void)setupWithTSGroup:(GroupModel*)model {
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
self.thread = [TSGroupThread threadWithGroupModel:model transaction:transaction];
self.thread = [TSGroupThread getOrCreateThreadWithGroupModel:model transaction:transaction];
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:self.thread messageBody:@"" attachments:nil];
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:self.thread messageBody:@"" attachments:[[NSMutableArray alloc] init]];
message.groupMetaMessage = TSGroupMessageNew;
[[TSMessagesManager sharedManager] sendMessage:message inThread:self.thread];
if(model.groupImage!=nil) {
[[TSMessagesManager sharedManager] sendAttachment:UIImagePNGRepresentation(model.groupImage) contentType:@"image/png" inMessage:message thread:self.thread];
}
else {
[[TSMessagesManager sharedManager] sendMessage:message inThread:self.thread];
}
isGroupConversation = YES;
}];
}
@ -102,6 +110,7 @@ typedef enum : NSUInteger {
- (void)setupWithThread:(TSThread *)thread{
self.thread = thread;
isGroupConversation = [self.thread isKindOfClass:[TSGroupThread class]];
}
- (void)viewDidLoad {
@ -117,7 +126,7 @@ typedef enum : NSUInteger {
[self.messageMappings updateWithTransaction:transaction];
}];
[self initializeNavigationBar];
[self initializeToolbars];
[self initializeCollectionViewLayout];
@ -160,21 +169,33 @@ typedef enum : NSUInteger {
#pragma mark - Initiliazers
-(void)initializeNavigationBar
-(void)initializeToolbars
{
self.title = self.thread.name;
if (!isGroupConversation && [self isRedPhoneReachable]) {
UIBarButtonItem *negativeSeparator = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
if (!isGroupConversation) {
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];
negativeSeparator.width = -8;
if ([self isRedPhoneReachable]) {
UIBarButtonItem * callButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"call_tab"] style:UIBarButtonItemStylePlain target:self action:@selector(callAction)];
[callButton setImageInsets:UIEdgeInsetsMake(0, -10, 0, -50)];
negativeSeparator.width = -8;
self.navigationItem.rightBarButtonItems = @[negativeSeparator, lockButton, callButton];
self.navigationItem.rightBarButtonItems = @[negativeSeparator, lockButton, callButton];
}
else {
self.navigationItem.rightBarButtonItem = lockButton;
}
} else {
UIBarButtonItem *groupMenuButton = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"settings_tab"] style:UIBarButtonItemStylePlain target:self action:@selector(didPressGroupMenuButton:)];
self.navigationItem.rightBarButtonItem = groupMenuButton;
if(![((TSGroupThread*)_thread).groupModel.groupMemberIds containsObject:[SignalKeyingStorage.localNumber toE164]]) {
[self inputToolbar].hidden= YES; // user has requested they leave the group. further sends disallowed
}
else {
UIBarButtonItem *groupMenuButton = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"settings_tab"] style:UIBarButtonItemStylePlain target:self action:@selector(didPressGroupMenuButton:)];
UIBarButtonItem *showGroupMembersButton = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"contacts_tab"] style:UIBarButtonItemStylePlain target:self action:@selector(showGroupMembers)];
self.navigationItem.rightBarButtonItems = @[negativeSeparator, groupMenuButton, showGroupMembersButton];
}
}
}
@ -212,6 +233,11 @@ typedef enum : NSUInteger {
}
-(void)showGroupMembers {
[self performSegueWithIdentifier:kShowGroupMembersSegue sender:self];
}
#pragma mark - Calls
-(BOOL)isRedPhoneReachable
@ -566,7 +592,7 @@ typedef enum : NSUInteger {
NSArray *actions = @[@"Accept new identity key", @"Copy new identity key to pasteboard"];
[self.inputToolbar.contentView resignFirstResponder];
[DJWActionSheet showInView:self.tabBarController.view withTitle:messageString cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete" otherButtonTitles:actions tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
NSLog(@"User Cancelled");
@ -594,7 +620,7 @@ typedef enum : NSUInteger {
#pragma mark - Navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:kFingerprintSegueIdentifier]){
FingerprintViewController *vc = [segue destinationViewController];
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
@ -607,6 +633,12 @@ typedef enum : NSUInteger {
[vc configWithThread:(TSGroupThread*)self.thread];
}];
}
else if([segue.identifier isEqualToString:kShowGroupMembersSegue]) {
ShowGroupMembersViewController *vc = [segue destinationViewController];
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
[vc configWithThread:(TSGroupThread*)self.thread];
}];
}
}
@ -767,8 +799,16 @@ typedef enum : NSUInteger {
return _editingDatabaseConnection;
}
- (void)yapDatabaseModified:(NSNotification *)notification
{
if(isGroupConversation) {
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
TSGroupThread* gThread = (TSGroupThread*)self.thread;
self.thread = [TSGroupThread threadWithGroupModel:gThread.groupModel transaction:transaction];
[self initializeToolbars];
}];
}
// Process the notification(s),
// and get the change-set(s) as applies to my view and mappings configuration.
NSArray *notifications = [self.uiDatabaseConnection beginLongLivedReadTransaction];
@ -863,7 +903,7 @@ typedef enum : NSUInteger {
withTitle:nil
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@[@"Update group", @"Leave group"]
otherButtonTitles:@[@"Update group", @"Leave group"] //@"Delete thread"] // TODOGROUP delete thread
tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
NSLog(@"User Cancelled");
@ -872,14 +912,15 @@ typedef enum : NSUInteger {
}else {
switch (tappedButtonIndex) {
case 0:
DDLogDebug(@"update group picked");
[self performSegueWithIdentifier:kUpdateGroupSegueIdentifier sender:self];
break;
case 1:
DDLogDebug(@"leave group picked");
[self leaveGroup];
break;
case 2:
DDLogDebug(@"delete thread");
//TODOGROUP delete thread
break;
default:
break;
}
@ -935,7 +976,6 @@ typedef enum : NSUInteger {
}];
}
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
if (action == @selector(delete:)) {
@ -955,6 +995,49 @@ typedef enum : NSUInteger {
}
}
- (void) leaveGroup {
TSGroupThread* gThread = (TSGroupThread*)_thread;
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:gThread messageBody:@"" attachments:[[NSMutableArray alloc] init]];
message.groupMetaMessage = TSGroupMessageQuit;
[[TSMessagesManager sharedManager] sendMessage:message inThread:gThread];
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSMutableArray *newGroupMemberIds = [NSMutableArray arrayWithArray:gThread.groupModel.groupMemberIds];
[newGroupMemberIds removeObject:[SignalKeyingStorage.localNumber toE164]];
gThread.groupModel.groupMemberIds = newGroupMemberIds;
[gThread saveWithTransaction:transaction];
}];
}
- (void) updateGroupModelTo:(GroupModel*)newGroupModel {
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSGroupThread* gThread = [TSGroupThread getOrCreateThreadWithGroupModel:newGroupModel transaction:transaction];
gThread.groupModel = newGroupModel;
[gThread saveWithTransaction:transaction];
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:gThread messageBody:@"" attachments:[[NSMutableArray alloc] init]];
message.groupMetaMessage = TSGroupMessageUpdate;
if(newGroupModel.groupImage!=nil) {
[[TSMessagesManager sharedManager] sendAttachment:UIImagePNGRepresentation(newGroupModel.groupImage) contentType:@"image/png" inMessage:message thread:gThread];
}
else {
[[TSMessagesManager sharedManager] sendMessage:message inThread:gThread];
}
self.thread = gThread;
}];
}
- (IBAction)unwindGroupUpdated:(UIStoryboardSegue *)segue{
[self.inputToolbar.contentView.textView resignFirstResponder];
NewGroupViewController *ngc = [segue sourceViewController];
GroupModel* newGroupModel = [ngc groupModel];
NSMutableArray* groupMemberIds = [[NSMutableArray alloc] initWithArray:newGroupModel.groupMemberIds];
[groupMemberIds addObject:[SignalKeyingStorage.localNumber toE164]];
newGroupModel.groupMemberIds = groupMemberIds;
[self updateGroupModelTo:newGroupModel];
}
- (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

@ -8,14 +8,15 @@
#import <UIKit/UIKit.h>
#import "TSGroupThread.h"
#import "GroupModel.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;
@property(nonatomic, strong) IBOutlet UIButton* groupImageButton;
@property(nonatomic, strong) IBOutlet UIView* tapToDismissView;
@property(nonatomic, strong) GroupModel* groupModel;
@end

@ -11,6 +11,8 @@
#import "Contact.h"
#import "ContactsManager.h"
#import "Environment.h"
#import "FunctionalUtil.h"
#import "Contact.h"
#import "GroupModel.h"
@ -23,6 +25,8 @@
#import <AVFoundation/AVFoundation.h>
#import <CoreMedia/CoreMedia.h>
static NSString* const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue";
@interface NewGroupViewController () {
NSArray* contacts;
}
@ -37,6 +41,21 @@
- (void)viewDidLoad {
[super viewDidLoad];
contacts = [Environment getCurrent].contactsManager.textSecureContacts;
contacts = [contacts filter:^int(Contact* contact) {
for(PhoneNumber* number in [contact parsedPhoneNumbers]) {
if([[number toE164] isEqualToString:[SignalKeyingStorage.localNumber toE164]]) {
return NO;
}
}
return YES;
}];
[self initializeDelegates];
[self initializeTableView];
[self initializeKeyboardHandlers];
if(_thread==nil) {
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Create" style:UIBarButtonItemStylePlain target:self action:@selector(createGroup)];
self.navigationItem.title = @"New Group";
@ -46,12 +65,25 @@
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;
[self setupGroupImageButton:_thread.groupModel.groupImage];
// Select the contacts already selected:
for (NSInteger r = 0; r < [_tableView numberOfRowsInSection:0]-1; r++) {
// TODOGROUP this will not scale well
NSMutableSet *usersInGroup = [NSMutableSet setWithArray:_thread.groupModel.groupMemberIds];
NSMutableArray *contactPhoneNumbers = [[NSMutableArray alloc] init];
for(PhoneNumber* number in [[contacts objectAtIndex:(NSUInteger)r] parsedPhoneNumbers]) {
[contactPhoneNumbers addObject:[number toE164]];
}
[usersInGroup intersectSet:[NSSet setWithArray:contactPhoneNumbers]];
if([usersInGroup count]>0) {
[_tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:(r+1) inSection:0]
animated:NO
scrollPosition:UITableViewScrollPositionNone];
}
}
}
contacts = [Environment getCurrent].contactsManager.textSecureContacts;
[self initializeDelegates];
[self initializeTableView];
[self initializeKeyboardHandlers];
}
- (void)didReceiveMemoryWarning {
@ -91,7 +123,15 @@
-(void)updateGroup {
DDLogDebug(@"Update gruop not implemented");
NSMutableArray* mut = [[NSMutableArray alloc]init];
for (NSIndexPath* idx in _tableView.indexPathsForSelectedRows) {
[mut addObjectsFromArray:[[contacts objectAtIndex:(NSUInteger)idx.row-1] textSecureIdentifiers]];
}
[mut addObject:[SignalKeyingStorage.localNumber toE164]]; // Also add the originator
_groupModel = [[GroupModel alloc] initWithTitle:_nameGroupTextField.text memberIds:[NSMutableArray arrayWithArray:[[NSSet setWithArray:mut] allObjects]] image:_groupImageButton.imageView.image groupId:_thread.groupModel.groupId];
[self performSegueWithIdentifier:kUnwindToMessagesViewSegue sender:self];
}
@ -112,6 +152,7 @@
-(IBAction)addGroupPhoto:(id)sender
{
[self.nameGroupTextField resignFirstResponder];
[DJWActionSheet showInView:self.parentViewController.view withTitle:nil cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil otherButtonTitles:@[@"Take a Picture",@"Choose from Library"]
tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
@ -182,16 +223,18 @@
UIImage *picture_camera = [info objectForKey:UIImagePickerControllerOriginalImage];
if (picture_camera) {
//There is a photo
_groupImageButton.imageView.image = picture_camera;
_groupImageButton.imageView.layer.cornerRadius = 40.0f;
_groupImageButton.imageView.clipsToBounds = YES;
[self setupGroupImageButton:picture_camera];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)setupGroupImageButton:(UIImage*)image {
_groupImageButton.imageView.image = image;
_groupImageButton.imageView.layer.cornerRadius = 4.0f;
_groupImageButton.imageView.clipsToBounds = YES;
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
@ -214,12 +257,6 @@
if (indexPath.row > 0) {
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];
@ -238,7 +275,6 @@
{
UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
@ -277,14 +313,4 @@
return fullNameAttributedString;
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end

@ -0,0 +1,18 @@
//
// ShowGroupMembersViewController.h
// Signal
//
// Created by Christine Corbett on 12/19/14
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "TSGroupThread.h"
#import "GroupModel.h"
@interface ShowGroupMembersViewController : UIViewController <UITableViewDelegate, UITabBarDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate>
- (void)configWithThread:(TSGroupThread*)thread;
@property(nonatomic, strong) IBOutlet UITableView* tableView;
@end

@ -0,0 +1,143 @@
//
// ShowGroupMembersViewController.m
// Signal
//
// Created by Dylan Bourgeois on 13/11/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "ShowGroupMembersViewController.h"
#import "SignalsViewController.h"
#import "Contact.h"
#import "ContactsManager.h"
#import "Environment.h"
#import "FunctionalUtil.h"
#import "Contact.h"
#import "GroupModel.h"
#import "SecurityUtils.h"
#import "SignalKeyingStorage.h"
#import "UIUtil.h"
#import "DJWActionSheet.h"
#import <MobileCoreServices/UTCoreTypes.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreMedia/CoreMedia.h>
static NSString* const kUnwindToMessagesViewSegue = @"UnwindToMessagesViewSegue";
@interface ShowGroupMembersViewController () {
NSArray* contacts;
}
@property TSGroupThread* thread;
@end
@implementation ShowGroupMembersViewController
- (void)configWithThread:(TSGroupThread *)gThread{
_thread = gThread;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.title = _thread.groupModel.groupName;
NSMutableArray *contactsInGroup = [[NSMutableArray alloc] init];
// Select the contacts already selected:
for (Contact* contact in [Environment getCurrent].contactsManager.textSecureContacts) {
// TODOGROUP this will not scale well; ~same code in NewGroupViewController
NSMutableSet *usersInGroup = [NSMutableSet setWithArray:_thread.groupModel.groupMemberIds];
NSMutableArray *contactPhoneNumbers = [[NSMutableArray alloc] init];
for(PhoneNumber* number in [contact parsedPhoneNumbers]) {
[contactPhoneNumbers addObject:[number toE164]];
}
[usersInGroup intersectSet:[NSSet setWithArray:contactPhoneNumbers]];
if([usersInGroup count]>0) {
[contactsInGroup addObject:contact];
}
}
contacts = contactsInGroup;
[self initializeTableView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
#pragma mark - Initializers
-(void)initializeTableView
{
self.tableView.tableFooterView = [[UIView alloc]initWithFrame:CGRectZero];
}
#pragma mark - Keyboard notifications
#pragma mark - Actions
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return (NSInteger)[contacts count]+1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SearchCell"];
if (cell == nil) {
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];
} else {
cell.textLabel.text = @"Group conversation Recipients:";
cell.textLabel.textColor = [UIColor lightGrayColor];
}
tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
return cell;
}
#pragma mark - Cell Utility
- (NSAttributedString *)attributedStringForContact:(Contact *)contact inCell:(UITableViewCell*)cell {
NSMutableAttributedString *fullNameAttributedString = [[NSMutableAttributedString alloc] initWithString:contact.fullName];
UIFont *firstNameFont;
UIFont *lastNameFont;
if (ABPersonGetSortOrdering() == kABPersonCompositeNameFormatFirstNameFirst) {
firstNameFont = [UIFont ows_lightFontWithSize:cell.textLabel.font.pointSize];
lastNameFont = [UIFont systemFontOfSize:cell.textLabel.font.pointSize];
} else{
firstNameFont = [UIFont ows_lightFontWithSize:cell.textLabel.font.pointSize];
lastNameFont = [UIFont systemFontOfSize:cell.textLabel.font.pointSize];
}
[fullNameAttributedString addAttribute:NSFontAttributeName value:firstNameFont range:NSMakeRange(0, contact.firstName.length)];
[fullNameAttributedString addAttribute:NSFontAttributeName value:lastNameFont range:NSMakeRange(contact.firstName.length + 1, contact.lastName.length)];
[fullNameAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, contact.fullName.length)];
return fullNameAttributedString;
}
@end

@ -25,7 +25,6 @@
[super viewDidLoad];
self.dbConnection = [TSStorageManager sharedManager].newDatabaseConnection;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(yapDatabaseModified:)
name:YapDatabaseModifiedNotification

@ -16,6 +16,9 @@
#import "TSDatabaseView.h"
#import "TSSocketManager.h"
#import "TSContactThread.h"
#import "TSMessagesManager+sendMessages.h"
#import "NSDate+millisecondTimeStamp.h"
#import <YapDatabase/YapDatabaseViewChange.h>
#import "YapDatabaseViewTransaction.h"
@ -131,7 +134,12 @@ static NSString *const kSegueIndentifier = @"showSegue";
- (void)tableViewCellTappedDelete:(InboxTableViewCell*)cell {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
TSThread *thread = [self threadForIndexPath:indexPath];
if([thread isKindOfClass:[TSGroupThread class]]) {
DDLogDebug(@"leaving the group");
TSOutgoingMessage *message = [[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp] inThread:thread messageBody:@"" attachments:[[NSMutableArray alloc] init]];
message.groupMetaMessage = TSGroupMessageQuit;
[[TSMessagesManager sharedManager] sendMessage:message inThread:thread];
}
[self.editingDbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[thread removeWithTransaction:transaction];
}];

@ -35,7 +35,7 @@
[super setUp];
[[TSStorageManager sharedManager].dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
self.thread = [TSContactThread threadWithContactId:@"aStupidId" transaction:transaction];
self.thread = [TSContactThread getOrCreateThreadWithContactId:@"aStupidId" transaction:transaction];
[self.thread saveWithTransaction:transaction];
}];

Loading…
Cancel
Save