Attachments handling

- Sends image rotated
- If message is delete, delete the attachment db object and file
- Delete attachment from detail view
This commit is contained in:
Frederic Jacobs 2014-12-26 23:18:54 +01:00
parent f2217cacd7
commit 402df72306
24 changed files with 275 additions and 81 deletions

View file

@ -348,6 +348,8 @@
B66DBF4A19D5BBC8006EA940 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B66DBF4919D5BBC8006EA940 /* Images.xcassets */; }; B66DBF4A19D5BBC8006EA940 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B66DBF4919D5BBC8006EA940 /* Images.xcassets */; };
B67ADDC41989FF8700E1A773 /* RPServerRequestsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B67ADDC31989FF8700E1A773 /* RPServerRequestsManager.m */; }; B67ADDC41989FF8700E1A773 /* RPServerRequestsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = B67ADDC31989FF8700E1A773 /* RPServerRequestsManager.m */; };
B67EBF5D19194AC60084CCFD /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = B67EBF5C19194AC60084CCFD /* Settings.bundle */; }; B67EBF5D19194AC60084CCFD /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = B67EBF5C19194AC60084CCFD /* Settings.bundle */; };
B68112EA1A4D9EC400BA82FF /* UIImage+normalizeImage.m in Sources */ = {isa = PBXBuildFile; fileRef = B68112E91A4D9EC400BA82FF /* UIImage+normalizeImage.m */; };
B68112ED1A4DA30300BA82FF /* JSQMessagesCollectionViewCell+menuBarItems.m in Sources */ = {isa = PBXBuildFile; fileRef = B68112EC1A4DA30300BA82FF /* JSQMessagesCollectionViewCell+menuBarItems.m */; };
B684A46D19C3446200B11029 /* PushManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B684A46C19C3446200B11029 /* PushManagerTest.m */; }; B684A46D19C3446200B11029 /* PushManagerTest.m in Sources */ = {isa = PBXBuildFile; fileRef = B684A46C19C3446200B11029 /* PushManagerTest.m */; };
B6850E5A1995A4710068E715 /* whisperFake.cer in Resources */ = {isa = PBXBuildFile; fileRef = B6850E591995A4710068E715 /* whisperFake.cer */; }; B6850E5A1995A4710068E715 /* whisperFake.cer in Resources */ = {isa = PBXBuildFile; fileRef = B6850E591995A4710068E715 /* whisperFake.cer */; };
B69CD25119773E79005CE69A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B69CD25019773E79005CE69A /* XCTest.framework */; }; B69CD25119773E79005CE69A /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B69CD25019773E79005CE69A /* XCTest.framework */; };
@ -972,6 +974,10 @@
B67ADDC21989FF8700E1A773 /* RPServerRequestsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RPServerRequestsManager.h; sourceTree = "<group>"; }; B67ADDC21989FF8700E1A773 /* RPServerRequestsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RPServerRequestsManager.h; sourceTree = "<group>"; };
B67ADDC31989FF8700E1A773 /* RPServerRequestsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RPServerRequestsManager.m; sourceTree = "<group>"; }; B67ADDC31989FF8700E1A773 /* RPServerRequestsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RPServerRequestsManager.m; sourceTree = "<group>"; };
B67EBF5C19194AC60084CCFD /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = SettingsBundle/Settings.bundle; sourceTree = SOURCE_ROOT; }; B67EBF5C19194AC60084CCFD /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = SettingsBundle/Settings.bundle; sourceTree = SOURCE_ROOT; };
B68112E81A4D9EC400BA82FF /* UIImage+normalizeImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+normalizeImage.h"; path = "util/UIImage+normalizeImage.h"; sourceTree = "<group>"; };
B68112E91A4D9EC400BA82FF /* UIImage+normalizeImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+normalizeImage.m"; path = "util/UIImage+normalizeImage.m"; sourceTree = "<group>"; };
B68112EB1A4DA30300BA82FF /* JSQMessagesCollectionViewCell+menuBarItems.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "JSQMessagesCollectionViewCell+menuBarItems.h"; path = "views/JSQMessagesCollectionViewCell+menuBarItems.h"; sourceTree = "<group>"; };
B68112EC1A4DA30300BA82FF /* JSQMessagesCollectionViewCell+menuBarItems.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "JSQMessagesCollectionViewCell+menuBarItems.m"; path = "views/JSQMessagesCollectionViewCell+menuBarItems.m"; sourceTree = "<group>"; };
B684A46C19C3446200B11029 /* PushManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PushManagerTest.m; path = Signal/test/push/PushManagerTest.m; sourceTree = SOURCE_ROOT; }; B684A46C19C3446200B11029 /* PushManagerTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PushManagerTest.m; path = Signal/test/push/PushManagerTest.m; sourceTree = SOURCE_ROOT; };
B6850E591995A4710068E715 /* whisperFake.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = whisperFake.cer; sourceTree = "<group>"; }; B6850E591995A4710068E715 /* whisperFake.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = whisperFake.cer; sourceTree = "<group>"; };
B69CD25019773E79005CE69A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; B69CD25019773E79005CE69A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
@ -2711,6 +2717,10 @@
FCFA64B31A24F3880007FB87 /* UIColor+OWS.m */, FCFA64B31A24F3880007FB87 /* UIColor+OWS.m */,
FCFA64B51A24F6730007FB87 /* UIFont+OWS.h */, FCFA64B51A24F6730007FB87 /* UIFont+OWS.h */,
FCFA64B61A24F6730007FB87 /* UIFont+OWS.m */, FCFA64B61A24F6730007FB87 /* UIFont+OWS.m */,
B68112E81A4D9EC400BA82FF /* UIImage+normalizeImage.h */,
B68112E91A4D9EC400BA82FF /* UIImage+normalizeImage.m */,
B68112EB1A4DA30300BA82FF /* JSQMessagesCollectionViewCell+menuBarItems.h */,
B68112EC1A4DA30300BA82FF /* JSQMessagesCollectionViewCell+menuBarItems.m */,
); );
name = "UI Categories"; name = "UI Categories";
path = ..; path = ..;
@ -3209,6 +3219,7 @@
76EB05EA18170B33006006FC /* CallProgress.m in Sources */, 76EB05EA18170B33006006FC /* CallProgress.m in Sources */,
FCFA64B41A24F3880007FB87 /* UIColor+OWS.m in Sources */, FCFA64B41A24F3880007FB87 /* UIColor+OWS.m in Sources */,
76EB05C218170B33006006FC /* DhPacketSharedSecretHashes.m in Sources */, 76EB05C218170B33006006FC /* DhPacketSharedSecretHashes.m in Sources */,
B68112ED1A4DA30300BA82FF /* JSQMessagesCollectionViewCell+menuBarItems.m in Sources */,
B6B096701A1D25ED008BFAA6 /* TSInfoMessage.m in Sources */, B6B096701A1D25ED008BFAA6 /* TSInfoMessage.m in Sources */,
B6C93C4E199567AD00EDF894 /* DebugLogger.m in Sources */, B6C93C4E199567AD00EDF894 /* DebugLogger.m in Sources */,
76EB063218170B33006006FC /* Crc32.m in Sources */, 76EB063218170B33006006FC /* Crc32.m in Sources */,
@ -3275,6 +3286,7 @@
B6B0968C1A1D25ED008BFAA6 /* TSDatabaseView.m in Sources */, B6B0968C1A1D25ED008BFAA6 /* TSDatabaseView.m in Sources */,
B6B0966A1A1D25ED008BFAA6 /* IncomingPushMessageSignal.pb.m in Sources */, B6B0966A1A1D25ED008BFAA6 /* IncomingPushMessageSignal.pb.m in Sources */,
BFB074C919A5611000F2947C /* ObservableValue.m in Sources */, BFB074C919A5611000F2947C /* ObservableValue.m in Sources */,
B68112EA1A4D9EC400BA82FF /* UIImage+normalizeImage.m in Sources */,
B6B0968E1A1D25ED008BFAA6 /* TSStorageManager.m in Sources */, B6B0968E1A1D25ED008BFAA6 /* TSStorageManager.m in Sources */,
FCB11D8A1A1284BB002F93FB /* SettingsTableViewCell.m in Sources */, FCB11D8A1A1284BB002F93FB /* SettingsTableViewCell.m in Sources */,
76EB05C818170B33006006FC /* HelloPacket.m in Sources */, 76EB05C818170B33006006FC /* HelloPacket.m in Sources */,

View file

@ -10,9 +10,10 @@ typedef NS_ENUM(NSUInteger, NotificationType) {
}; };
typedef NS_ENUM(NSUInteger, TSImageQuality) { typedef NS_ENUM(NSUInteger, TSImageQuality) {
TSImageQualityHigh, TSImageQualityUncropped = 1,
TSImageQualityMedium, TSImageQualityHigh = 2,
TSImageQualityLow TSImageQualityMedium = 3,
TSImageQualityLow = 4
}; };
@class PhoneNumber; @class PhoneNumber;

View file

@ -135,7 +135,7 @@
if (preference) { if (preference) {
return [preference unsignedIntegerValue]; return [preference unsignedIntegerValue];
} else { } else {
return TSImageQualityMedium; return TSImageQualityUncropped;
} }
} }

View file

@ -7,8 +7,6 @@
// //
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#import <YapDatabase/YapDatabaseRelationshipNode.h>
#import "TSYapDatabaseObject.h" #import "TSYapDatabaseObject.h"
typedef NS_ENUM(NSInteger, TSLastActionType) { typedef NS_ENUM(NSInteger, TSLastActionType) {

View file

@ -8,7 +8,7 @@
#import "TSAttachment.h" #import "TSAttachment.h"
@interface TSAttachmentStream : TSAttachment @interface TSAttachmentStream : TSAttachment <YapDatabaseRelationshipNode>
- (instancetype)initWithIdentifier:(NSString*)identifier - (instancetype)initWithIdentifier:(NSString*)identifier
data:(NSData*)data data:(NSData*)data

View file

@ -7,10 +7,13 @@
// //
#import "TSAttachmentStream.h" #import "TSAttachmentStream.h"
#import "UIImage+contentTypes.h"
NSString * const TSAttachementFileRelationshipEdge = @"TSAttachementFileEdge";
@interface TSAttachmentStream () @interface TSAttachmentStream ()
@property (nonatomic) NSString *path; @property (nonatomic) NSString *attachmentPath;
@end @end
@ -21,9 +24,8 @@
key:(NSData*)key key:(NSData*)key
contentType:(NSString*)contentType{ contentType:(NSString*)contentType{
self = [super initWithIdentifier:identifier encryptionKey:key contentType:contentType]; self = [super initWithIdentifier:identifier encryptionKey:key contentType:contentType];
NSString *path = [self filePath]; [[NSFileManager defaultManager] createFileAtPath:_attachmentPath contents:data attributes:nil];
[[NSFileManager defaultManager] createFileAtPath:path contents:data attributes:nil];
return self; return self;
} }
@ -32,10 +34,18 @@
return YES; return YES;
} }
- (NSArray *)yapDatabaseRelationshipEdges {
YapDatabaseRelationshipEdge *attachmentFileEdge = [YapDatabaseRelationshipEdge edgeWithName:TSAttachementFileRelationshipEdge
destinationFilePath:[self filePath]
nodeDeleteRules:YDB_DeleteDestinationIfSourceDeleted];
return @[attachmentFileEdge];
}
+ (NSString*)attachmentsFolder { + (NSString*)attachmentsFolder {
NSFileManager* fileManager = [NSFileManager defaultManager]; NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL *fileURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; NSURL *fileURL = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSString *path = [fileURL path]; NSString *path = [fileURL path];
NSString *attachmentFolder = [path stringByAppendingFormat:@"/Attachments"]; NSString *attachmentFolder = [path stringByAppendingFormat:@"/Attachments"];
NSError * error = nil; NSError * error = nil;
@ -46,7 +56,7 @@
if (error != nil) { if (error != nil) {
DDLogError(@"Failed to create attachments directory: %@", error.description); DDLogError(@"Failed to create attachments directory: %@", error.description);
} }
return attachmentFolder; return attachmentFolder;
} }
@ -75,7 +85,7 @@
return nil; return nil;
} }
return [UIImage imageWithContentsOfFile:[self filePath]]; return [UIImage imageWithContentsOfFile:self.filePath];
} }
@end @end

View file

@ -33,6 +33,6 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage){
- (void)addattachments:(NSArray*)attachments; - (void)addattachments:(NSArray*)attachments;
- (void)addattachment:(NSString*)attachment; - (void)addattachment:(NSString*)attachment;
- (BOOL)hasattachments; - (BOOL)hasAttachments;
@end @end

View file

@ -7,7 +7,9 @@
// //
#import "TSMessage.h" #import "TSMessage.h"
#import "TSAttachment.h"
NSString * const TSAttachementsRelationshipEdgeName = @"TSAttachmentEdge";
@implementation TSMessage @implementation TSMessage
@ -25,10 +27,25 @@
[self.attachments addObject:attachment]; [self.attachments addObject:attachment];
} }
- (NSArray *)yapDatabaseRelationshipEdges {
NSMutableArray *edges = [[super yapDatabaseRelationshipEdges] mutableCopy];
if ([self hasAttachments]) {
for (NSString *attachmentId in self.attachments) {
YapDatabaseRelationshipEdge *fileEdge = [[YapDatabaseRelationshipEdge alloc] initWithName:TSAttachementsRelationshipEdgeName
destinationKey:attachmentId
collection:[TSAttachment collection]
nodeDeleteRules:YDB_DeleteDestinationIfAllSourcesDeleted];
[edges addObject:fileEdge];
}
}
return edges;
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp - (instancetype)initWithTimestamp:(uint64_t)timestamp
inThread:(TSThread*)thread inThread:(TSThread*)thread
messageBody:(NSString*)body messageBody:(NSString*)body
attachments:(NSArray*)attachments attachments:(NSArray*)attachments
{ {
self = [super initWithTimestamp:timestamp inThread:thread]; self = [super initWithTimestamp:timestamp inThread:thread];
@ -39,13 +56,13 @@
return self; return self;
} }
- (BOOL)hasattachments{ - (BOOL)hasAttachments{
return self.attachments?(self.attachments.count>0):false; return self.attachments?(self.attachments.count>0):false;
} }
- (NSString *)description{ - (NSString *)description{
if(self.attachments > 0){ if(self.attachments > 0){
return @"attachment"; return @"Attachment";
} else { } else {
return self.body; return self.body;
} }

View file

@ -46,6 +46,7 @@ dispatch_queue_t attachmentsQueue() {
for (PushMessageContentAttachmentPointer *pointer in content.attachments) { for (PushMessageContentAttachmentPointer *pointer in content.attachments) {
TSAttachmentPointer *attachmentPointer = [[TSAttachmentPointer alloc] initWithIdentifier:pointer.id key:pointer.key contentType:pointer.contentType relay:message.relay]; TSAttachmentPointer *attachmentPointer = [[TSAttachmentPointer alloc] initWithIdentifier:pointer.id key:pointer.key contentType:pointer.contentType relay:message.relay];
[attachmentPointer saveWithTransaction:transaction]; [attachmentPointer saveWithTransaction:transaction];
dispatch_async(attachmentsQueue(), ^{ dispatch_async(attachmentsQueue(), ^{
[self retrieveAttachment:attachmentPointer]; [self retrieveAttachment:attachmentPointer];
}); });
@ -57,7 +58,7 @@ dispatch_queue_t attachmentsQueue() {
} }
- (void)sendAttachment:(NSData*)attachmentData contentType:(NSString*)contentType thread:(TSThread*)thread { - (void)sendAttachment:(NSData*)attachmentData contentType:(NSString*)contentType thread:(TSThread*)thread {
TSRequest *allocateAttachment = [[TSAllocAttachmentRequest alloc] init]; TSRequest *allocateAttachment = [[TSAllocAttachmentRequest alloc] init];
[[TSNetworkManager sharedManager] queueAuthenticatedRequest:allocateAttachment success:^(NSURLSessionDataTask *task, id responseObject) { [[TSNetworkManager sharedManager] queueAuthenticatedRequest:allocateAttachment success:^(NSURLSessionDataTask *task, id responseObject) {
dispatch_async(attachmentsQueue(), ^{ dispatch_async(attachmentsQueue(), ^{
@ -174,4 +175,5 @@ dispatch_queue_t attachmentsQueue() {
return success; return success;
} }
@end @end

View file

@ -0,0 +1,15 @@
//
// UIImage+normalizeImage.h
// Signal
//
// Created by Frederic Jacobs on 26/12/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIImage (normalizeImage)
- (UIImage *)normalizedImage;
@end

View file

@ -0,0 +1,23 @@
//
// UIImage+normalizeImage.m
// Signal
//
// Created by Frederic Jacobs on 26/12/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "UIImage+normalizeImage.h"
@implementation UIImage (normalizeImage)
- (UIImage *)normalizedImage {
if (self.imageOrientation == UIImageOrientationUp) return self;
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
[self drawInRect:(CGRect){0, 0, self.size}];
UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return normalizedImage;
}
@end

View file

@ -2,6 +2,8 @@
#import "UIColor+OWS.h" #import "UIColor+OWS.h"
#import "UIFont+OWS.h" #import "UIFont+OWS.h"
#import "UIImage+normalizeImage.h"
#import "UIImage+contentTypes.h"
/** /**
* *

View file

@ -4,7 +4,6 @@
@implementation UIUtil @implementation UIUtil
+ (void)applyRoundedBorderToImageView:(UIImageView *__strong*)imageView { + (void)applyRoundedBorderToImageView:(UIImageView *__strong*)imageView {
[[*imageView layer] setBorderWidth:CONTACT_PICTURE_VIEW_BORDER_WIDTH]; [[*imageView layer] setBorderWidth:CONTACT_PICTURE_VIEW_BORDER_WIDTH];
[[*imageView layer] setBorderColor:[[UIColor lightGrayColor] CGColor]]; [[*imageView layer] setBorderColor:[[UIColor lightGrayColor] CGColor]];

View file

@ -7,11 +7,13 @@
// //
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "TSAttachmentStream.h"
#import "TSInteraction.h"
@interface FullImageViewController : UIViewController @interface FullImageViewController : UIViewController
- (instancetype)initWithImage:(UIImage*)image fromRect:(CGRect)rect; - (instancetype)initWithAttachment:(TSAttachmentStream*)attachment fromRect:(CGRect)rect forInteraction:(TSInteraction*)interaction;
-(void)presentFromViewController:(UIViewController*)viewController; - (void)presentFromViewController:(UIViewController*)viewController;
@end @end

View file

@ -8,6 +8,7 @@
#import "FullImageViewController.h" #import "FullImageViewController.h"
#import "DJWActionSheet.h" #import "DJWActionSheet.h"
#import "TSAttachmentStream.h"
#define kImageViewCornerRadius 5.0f #define kImageViewCornerRadius 5.0f
@ -24,7 +25,6 @@
@property (nonatomic, strong) UIScrollView *scrollView; @property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIImageView *imageView; @property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIImage* image;
@property (nonatomic, strong) UITapGestureRecognizer *singleTap; @property (nonatomic, strong) UITapGestureRecognizer *singleTap;
@property (nonatomic, strong) UITapGestureRecognizer *doubleTap; @property (nonatomic, strong) UITapGestureRecognizer *doubleTap;
@ -34,23 +34,31 @@
@property CGRect originRect; @property CGRect originRect;
@property BOOL isPresenting; @property BOOL isPresenting;
@property TSAttachmentStream *attachment;
@property TSInteraction *interaction;
@end @end
@implementation FullImageViewController @implementation FullImageViewController
- (instancetype)initWithImage:(UIImage*)image fromRect:(CGRect)rect { - (instancetype)initWithAttachment:(TSAttachmentStream*)attachment fromRect:(CGRect)rect forInteraction:(TSInteraction*)interaction {
self = [super initWithNibName:nil bundle:nil]; self = [super initWithNibName:nil bundle:nil];
if (self) { if (self) {
self.image = image; self.attachment = attachment;
self.imageView.image = image; self.imageView.image = self.image;
self.originRect = rect; self.originRect = rect;
self.interaction = interaction;
} }
return self; return self;
} }
- (UIImage*)image{
return self.attachment.image;
}
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
@ -353,11 +361,14 @@
{ {
[DJWActionSheet showInView:self.view withTitle:nil cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete" otherButtonTitles:@[@"Save to Camera Roll", @"Copy"] tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { [DJWActionSheet showInView:self.view withTitle:nil cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete" otherButtonTitles:@[@"Save to Camera Roll", @"Copy"] tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
if (tappedButtonIndex == actionSheet.cancelButtonIndex) { if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
} else if (tappedButtonIndex == actionSheet.destructiveButtonIndex){
__block TSInteraction *interaction = [self interaction];
[self dismissViewControllerAnimated:YES completion:^{
[interaction remove];
}];
} else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { } else {
#warning Unimplemented deleting attachments from FullImageView
NSLog(@"Destructive button tapped");
}else {
switch (tappedButtonIndex) { switch (tappedButtonIndex) {
case 0: case 0:
UIImageWriteToSavedPhotosAlbum(self.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil); UIImageWriteToSavedPhotosAlbum(self.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

View file

@ -11,11 +11,12 @@
#import "GroupModel.h" #import "GroupModel.h"
@class TSThread; @class TSThread;
@interface MessagesViewController : JSQMessagesViewController <UIImagePickerControllerDelegate,UINavigationControllerDelegate> @interface MessagesViewController : JSQMessagesViewController <UIImagePickerControllerDelegate,
UINavigationControllerDelegate,
UITextViewDelegate>
- (void)setupWithThread:(TSThread*)thread; - (void)setupWithThread:(TSThread*)thread;
- (void)setupWithTSIdentifier:(NSString*)identifier; - (void)setupWithTSIdentifier:(NSString*)identifier;
- (void)setupWithTSGroup:(GroupModel*)model; - (void)setupWithTSGroup:(GroupModel*)model;
@end @end

View file

@ -133,7 +133,6 @@ typedef enum : NSUInteger {
-(void)viewWillAppear:(BOOL)animated -(void)viewWillAppear:(BOOL)animated
{ {
[super viewWillAppear:animated]; [super viewWillAppear:animated];
self.automaticallyScrollsToMostRecentMessage = YES;
[self scrollToBottomAnimated:NO]; [self scrollToBottomAnimated:NO];
} }
@ -164,7 +163,7 @@ typedef enum : NSUInteger {
-(void)initializeNavigationBar -(void)initializeNavigationBar
{ {
self.title = self.thread.name; self.title = self.thread.name;
if (!isGroupConversation && [self isRedPhoneReachable]) { if (!isGroupConversation && [self isRedPhoneReachable]) {
UIBarButtonItem * lockButton = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:@"lock"] style:UIBarButtonItemStylePlain target:self action:@selector(showFingerprint)]; 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)]; UIBarButtonItem * callButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"call_tab"] style:UIBarButtonItemStylePlain target:self action:@selector(callAction)];
@ -197,7 +196,7 @@ typedef enum : NSUInteger {
self.collectionView.showsVerticalScrollIndicator = NO; self.collectionView.showsVerticalScrollIndicator = NO;
self.collectionView.showsHorizontalScrollIndicator = NO; self.collectionView.showsHorizontalScrollIndicator = NO;
self.automaticallyScrollsToMostRecentMessage = NO; self.automaticallyScrollsToMostRecentMessage = YES;
self.collectionView.collectionViewLayout.incomingAvatarViewSize = CGSizeZero; self.collectionView.collectionViewLayout.incomingAvatarViewSize = CGSizeZero;
self.collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero; self.collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero;
@ -298,24 +297,18 @@ typedef enum : NSUInteger {
switch (msg.messageType) { switch (msg.messageType) {
case TSIncomingMessageAdapter: case TSIncomingMessageAdapter:
return [self loadIncomingMessageCellForMessage:msg atIndexPath:indexPath]; return [self loadIncomingMessageCellForMessage:msg atIndexPath:indexPath];
break;
case TSOutgoingMessageAdapter: case TSOutgoingMessageAdapter:
return [self loadOutgoingCellForMessage:msg atIndexPath:indexPath]; return [self loadOutgoingCellForMessage:msg atIndexPath:indexPath];
break;
case TSCallAdapter: case TSCallAdapter:
return [self loadCallCellForCall:msg atIndexPath:indexPath]; return [self loadCallCellForCall:msg atIndexPath:indexPath];
break;
case TSInfoMessageAdapter: case TSInfoMessageAdapter:
return [self loadInfoMessageCellForMessage:msg atIndexPath:indexPath]; return [self loadInfoMessageCellForMessage:msg atIndexPath:indexPath];
break;
case TSErrorMessageAdapter: case TSErrorMessageAdapter:
return [self loadErrorMessageCellForMessage:msg atIndexPath:indexPath]; return [self loadErrorMessageCellForMessage:msg atIndexPath:indexPath];
break;
default: default:
NSLog(@"Something went wrong"); NSLog(@"Something went wrong");
return nil; return nil;
break;
} }
} }
@ -325,7 +318,8 @@ typedef enum : NSUInteger {
{ {
JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:self.collectionView cellForItemAtIndexPath:indexPath]; JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:self.collectionView cellForItemAtIndexPath:indexPath];
if (!message.isMediaMessage) { if (!message.isMediaMessage) {
cell.textView.textColor = [UIColor blackColor]; cell.textView.textColor = [UIColor blackColor];
cell.textView.selectable = NO;
cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor, cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor,
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) }; NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) };
} }
@ -338,7 +332,8 @@ typedef enum : NSUInteger {
JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:self.collectionView cellForItemAtIndexPath:indexPath]; JSQMessagesCollectionViewCell *cell = (JSQMessagesCollectionViewCell *)[super collectionView:self.collectionView cellForItemAtIndexPath:indexPath];
if (!message.isMediaMessage) if (!message.isMediaMessage)
{ {
cell.textView.textColor = [UIColor whiteColor]; cell.textView.textColor = [UIColor whiteColor];
cell.textView.selectable = NO;
cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor, cell.textView.linkTextAttributes = @{ NSForegroundColorAttributeName : cell.textView.textColor,
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) }; NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle | NSUnderlinePatternSolid) };
} }
@ -497,6 +492,7 @@ typedef enum : NSUInteger {
TSMessageAdapter *messageItem = [collectionView.dataSource collectionView:collectionView messageDataForItemAtIndexPath:indexPath]; TSMessageAdapter *messageItem = [collectionView.dataSource collectionView:collectionView messageDataForItemAtIndexPath:indexPath];
TSInteraction *interaction = [self interactionAtIndexPath:indexPath]; TSInteraction *interaction = [self interactionAtIndexPath:indexPath];
switch (messageItem.messageType) { switch (messageItem.messageType) {
case TSOutgoingMessageAdapter: case TSOutgoingMessageAdapter:
if (messageItem.messageState == TSOutgoingMessageStateUnsent) { if (messageItem.messageState == TSOutgoingMessageStateUnsent) {
@ -507,15 +503,21 @@ typedef enum : NSUInteger {
BOOL isMediaMessage = [messageItem isMediaMessage]; BOOL isMediaMessage = [messageItem isMediaMessage];
if (isMediaMessage) { if (isMediaMessage) {
TSAttachmentAdapter * messageMedia = (TSAttachmentAdapter*)[messageItem media]; TSAttachmentAdapter* messageMedia = (TSAttachmentAdapter*)[messageItem media];
if ([messageMedia isImage]) { if ([messageMedia isImage]) {
//is a photo tappedImage = ((UIImageView*)[messageMedia mediaView]).image;
tappedImage = ((UIImageView*)[messageMedia mediaView]).image ;
CGRect convertedRect = [self.collectionView convertRect:[collectionView cellForItemAtIndexPath:indexPath].frame toView:nil]; CGRect convertedRect = [self.collectionView convertRect:[collectionView cellForItemAtIndexPath:indexPath].frame toView:nil];
FullImageViewController * vc = [[FullImageViewController alloc]initWithImage:tappedImage fromRect:convertedRect]; __block TSAttachment *attachment = nil;
[vc presentFromViewController:self]; [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
attachment = [TSAttachment fetchObjectWithUniqueID:messageMedia.attachmentId transaction:transaction];
}];
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *attStream = (TSAttachmentStream*)attachment;
FullImageViewController * vc = [[FullImageViewController alloc] initWithAttachment:attStream fromRect:convertedRect forInteraction:[self interactionAtIndexPath:indexPath]];
[vc presentFromViewController:self];
}
} else { } else {
DDLogWarn(@"Currently unsupported"); DDLogWarn(@"Currently unsupported");
} }
@ -550,6 +552,13 @@ typedef enum : NSUInteger {
}]; }];
} }
- (void)deleteMessageAtIndexPath:(NSIndexPath*)indexPath {
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
TSInteraction *interaction = [self interactionAtIndexPath:indexPath];
[interaction removeWithTransaction:transaction];
}];
}
- (void)handleErrorMessageTap:(TSErrorMessage*)message{ - (void)handleErrorMessageTap:(TSErrorMessage*)message{
if (message.errorType == TSErrorMessageWrongTrustedIdentityKey) { if (message.errorType == TSErrorMessageWrongTrustedIdentityKey) {
NSString *newKeyFingerprint = [message newIdentityKey]; NSString *newKeyFingerprint = [message newIdentityKey];
@ -615,9 +624,8 @@ typedef enum : NSUInteger {
picker.sourceType = UIImagePickerControllerSourceTypeCamera; picker.sourceType = UIImagePickerControllerSourceTypeCamera;
if ([UIImagePickerController isSourceTypeAvailable: if ([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeCamera]) UIImagePickerControllerSourceTypeCamera]) {
{ picker.mediaTypes = @[(NSString*)kUTTypeImage];
picker.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *)kUTTypeMovie, kUTTypeImage, kUTTypeVideo, nil];
[self presentViewController:picker animated:YES completion:NULL]; [self presentViewController:picker animated:YES completion:NULL];
} }
@ -655,7 +663,7 @@ typedef enum : NSUInteger {
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info -(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{ {
UIImage *picture_camera = [info objectForKey:UIImagePickerControllerOriginalImage]; UIImage *picture_camera = [[info objectForKey:UIImagePickerControllerOriginalImage] normalizedImage];
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType]; NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
@ -680,6 +688,9 @@ typedef enum : NSUInteger {
{ {
CGFloat correctedWidth; CGFloat correctedWidth;
switch ([Environment.preferences imageUploadQuality]) { switch ([Environment.preferences imageUploadQuality]) {
case TSImageQualityUncropped:
return image;
case TSImageQualityHigh: case TSImageQualityHigh:
correctedWidth = 2048; correctedWidth = 2048;
break; break;
@ -721,15 +732,14 @@ typedef enum : NSUInteger {
-(CGFloat)compressionRate -(CGFloat)compressionRate
{ {
switch ([Environment.preferences imageUploadQuality]) { switch ([Environment.preferences imageUploadQuality]) {
case TSImageQualityUncropped:
return 1;
case TSImageQualityHigh: case TSImageQualityHigh:
return 0.9f; return 0.9f;
break;
case TSImageQualityMedium: case TSImageQualityMedium:
return 0.5f; return 0.5f;
break;
case TSImageQualityLow: case TSImageQualityLow:
return 0.3f; return 0.3f;
break;
default: default:
break; break;
} }
@ -853,7 +863,7 @@ typedef enum : NSUInteger {
withTitle:nil withTitle:nil
cancelButtonTitle:@"Cancel" cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil destructiveButtonTitle:nil
otherButtonTitles:@[@"Update group", @"Leave group", @"Delete thread"] otherButtonTitles:@[@"Update group", @"Leave group"]
tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
if (tappedButtonIndex == actionSheet.cancelButtonIndex) { if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
NSLog(@"User Cancelled"); NSLog(@"User Cancelled");
@ -867,12 +877,9 @@ typedef enum : NSUInteger {
break; break;
case 1: case 1:
DDLogDebug(@"leave group picket"); DDLogDebug(@"leave group picked");
break; break;
case 2:
DDLogDebug(@"delete thread");
break;
default: default:
break; break;
} }
@ -893,12 +900,12 @@ typedef enum : NSUInteger {
withTitle:nil withTitle:nil
cancelButtonTitle:@"Cancel" cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil destructiveButtonTitle:nil
otherButtonTitles:@[@"Take Photo or Video", @"Choose existing Photo", @"Choose existing Video", @"Send file"] otherButtonTitles:@[@"Take Photo", @"Choose existing Photo"]
tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
if (tappedButtonIndex == actionSheet.cancelButtonIndex) { if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
NSLog(@"User Cancelled"); DDLogVerbose(@"User Cancelled");
} else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) { } else if (tappedButtonIndex == actionSheet.destructiveButtonIndex) {
NSLog(@"Destructive button tapped"); DDLogVerbose(@"Destructive button tapped");
}else { }else {
switch (tappedButtonIndex) { switch (tappedButtonIndex) {
case 0: case 0:
@ -907,10 +914,6 @@ typedef enum : NSUInteger {
case 1: case 1:
[self chooseFromLibrary:kMediaTypePicture]; [self chooseFromLibrary:kMediaTypePicture];
break; break;
case 2:
[self chooseFromLibrary:kMediaTypeVideo];
break;
default: default:
break; break;
} }
@ -932,6 +935,26 @@ typedef enum : NSUInteger {
}]; }];
} }
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
if (action == @selector(delete:)) {
return YES;
}
return [super collectionView:collectionView canPerformAction:action forItemAtIndexPath:indexPath withSender:sender];
}
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
{
if (action == @selector(delete:)) {
[self deleteMessageAtIndexPath:indexPath];
}
else {
[super collectionView:collectionView performAction:action forItemAtIndexPath:indexPath withSender:sender];
}
}
- (void)dealloc{ - (void)dealloc{
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
} }

View file

@ -43,6 +43,9 @@
-(void)updateImageQualityLabel -(void)updateImageQualityLabel
{ {
switch ([Environment.preferences imageUploadQuality]) { switch ([Environment.preferences imageUploadQuality]) {
case TSImageQualityUncropped:
self.detailLabel.text = @"Full";
break;
case TSImageQualityHigh: case TSImageQualityHigh:
self.detailLabel.text = @"High"; self.detailLabel.text = @"High";
break; break;

View file

@ -125,7 +125,7 @@ typedef enum {
withTitle:nil withTitle:nil
cancelButtonTitle:@"Cancel" cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil destructiveButtonTitle:nil
otherButtonTitles:@[@"High", @"Medium", @"Low"] otherButtonTitles:@[@"Uncompressed", @"High", @"Medium", @"Low"]
tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) { tapBlock:^(DJWActionSheet *actionSheet, NSInteger tappedButtonIndex) {
[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
if (tappedButtonIndex == actionSheet.cancelButtonIndex) { if (tappedButtonIndex == actionSheet.cancelButtonIndex) {
@ -136,12 +136,15 @@ typedef enum {
}else { }else {
switch (tappedButtonIndex) { switch (tappedButtonIndex) {
case 0: case 0:
[Environment.preferences setImageUploadQuality:TSImageQualityHigh]; [Environment.preferences setImageUploadQuality:TSImageQualityUncropped];
break; break;
case 1: case 1:
[Environment.preferences setImageUploadQuality:TSImageQualityMedium]; [Environment.preferences setImageUploadQuality:TSImageQualityHigh];
break; break;
case 2: case 2:
[Environment.preferences setImageUploadQuality:TSImageQualityMedium];
break;
case 3:
[Environment.preferences setImageUploadQuality:TSImageQualityLow]; [Environment.preferences setImageUploadQuality:TSImageQualityLow];
break; break;
default: default:

View file

@ -16,4 +16,6 @@
- (BOOL)isImage; - (BOOL)isImage;
@property NSString *attachmentId;
@end @end

View file

@ -16,8 +16,6 @@
@property UIImage *image; @property UIImage *image;
@property (strong, nonatomic) UIImageView *cachedImageView; @property (strong, nonatomic) UIImageView *cachedImageView;
@property (assign, nonatomic, readonly) BOOL isImageAttachment;
@end @end
@implementation TSAttachmentAdapter @implementation TSAttachmentAdapter
@ -26,9 +24,9 @@
self = [super init]; self = [super init];
if (self) { if (self) {
_image = [UIImage imageWithCGImage:attachment.image.CGImage]; _image = attachment.image;
_cachedImageView = nil; _cachedImageView = nil;
_isImageAttachment = YES; _attachmentId = attachment.uniqueId;
} }
return self; return self;
} }
@ -73,7 +71,7 @@
-(BOOL)isImage -(BOOL)isImage
{ {
return _isImageAttachment; return YES;
} }
#pragma mark - Utility #pragma mark - Utility
@ -100,7 +98,6 @@
return ratio > 1.0f ? [self smallPortraitSize] : [self smallLandscapeSize]; return ratio > 1.0f ? [self smallPortraitSize] : [self smallLandscapeSize];
} }
- (CGSize)largePortraitSize - (CGSize)largePortraitSize
{ {
return CGSizeMake(220.0f, 310.0f); return CGSizeMake(220.0f, 310.0f);

View file

@ -15,6 +15,7 @@
#import "TSattachment.h" #import "TSattachment.h"
#import "TSAttachmentStream.h" #import "TSAttachmentStream.h"
#import "TSAttachmentAdapter.h" #import "TSAttachmentAdapter.h"
#import "TSAttachmentPointer.h"
@interface TSMessageAdapter () @interface TSMessageAdapter ()
@ -92,13 +93,23 @@
for (NSString *attachmentID in message.attachments) { for (NSString *attachmentID in message.attachments) {
TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentID]; TSAttachment *attachment = [TSAttachment fetchObjectWithUniqueID:attachmentID];
if ([attachment isKindOfClass:[TSAttachmentStream class]]) { if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *stream = (TSAttachmentStream*)attachment; TSAttachmentStream *stream = (TSAttachmentStream*)attachment;
if ([stream isImage]) { if ([stream isImage]) {
adapter.mediaItem = [[TSAttachmentAdapter alloc] initWithAttachment:stream]; adapter.mediaItem = [[TSAttachmentAdapter alloc] initWithAttachment:stream];
adapter.mediaItem.appliesMediaViewMaskAsOutgoing = [interaction isKindOfClass:[TSOutgoingMessage class]]; adapter.mediaItem.appliesMediaViewMaskAsOutgoing = [interaction isKindOfClass:[TSOutgoingMessage class]];
break;
} else {
DDLogWarn(@"We have a TSAttachmentStream for an unsupported media type");
} }
} else if ([attachment isKindOfClass:[TSAttachmentPointer class]]){
//TSAttachmentPointer *pointer = (TSAttachmentPointer*)attachment;
//TODO: Change this status when download failed;
adapter.messageBody = @"Attachment is downloading";
adapter.messageType = TSInfoMessageAdapter;
} else {
DDLogError(@"We retreived an attachment that doesn't have a known type : %@", NSStringFromClass([attachment class]));
} }
} }
} }

View file

@ -0,0 +1,13 @@
//
// JSQMessagesCollectionViewCell+menuBarItems.h
// Signal
//
// Created by Frederic Jacobs on 26/12/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "JSQMessagesCollectionViewCell.h"
@interface JSQMessagesCollectionViewCell (menuBarItems)
@end

View file

@ -0,0 +1,49 @@
//
// JSQMessagesCollectionViewCell+menuBarItems.m
// Signal
//
// Created by Frederic Jacobs on 26/12/14.
// Copyright (c) 2014 Open Whisper Systems. All rights reserved.
//
#import "JSQMessagesCollectionViewCell+menuBarItems.h"
@implementation JSQMessagesCollectionViewCell (menuBarItems)
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
if (action == @selector(delete:)) {
return YES;
}
return [super canPerformAction:action withSender:sender];
}
- (void)delete:(id)sender
{
[self performSelectorOnParentCollectionView:@selector(delete:)
withSender:sender];
}
- (void)performSelectorOnParentCollectionView:(SEL)selector
withSender:(id)sender {
UIView *view = self;
do {
view = view.superview;
} while (![view isKindOfClass:[UICollectionView class]]);
UICollectionView *collectionView = (UICollectionView *)view;
NSIndexPath *indexPath = [collectionView indexPathForCell:self];
if (collectionView.delegate &&
[collectionView.delegate respondsToSelector:@selector(collectionView:
performAction:
forItemAtIndexPath:
withSender:)])
[collectionView.delegate collectionView:collectionView
performAction:selector
forItemAtIndexPath:indexPath
withSender:sender];
}
@end