WIP: towards avatar attachment streams

// FREEBIE
pull/1/head
Michael Kirk 8 years ago
parent 53af41fcc6
commit 0b8b3b4f16

@ -1 +1 @@
Subproject commit 7d20c06f10623230848be112f493804b108e6aa1 Subproject commit e54eb900c0c4be9646d4c7ed800c8ea45d275686

@ -209,11 +209,9 @@ NS_ASSUME_NONNULL_BEGIN
if (!self.hasQuotedAttachmentThumbnailImage) { if (!self.hasQuotedAttachmentThumbnailImage) {
return nil; return nil;
} }
if (!self.quotedMessage.thumbnailData) {
return nil;
}
// TODO: Possibly ignore data that is too large. // TODO: Possibly ignore data that is too large.
UIImage *_Nullable image = [UIImage imageWithData:self.quotedMessage.thumbnailData]; UIImage *_Nullable image = self.quotedMessage.thumbnailImage;
// TODO: Possibly ignore images that are too large. // TODO: Possibly ignore images that are too large.
return image; return image;
} }
@ -241,7 +239,7 @@ NS_ASSUME_NONNULL_BEGIN
NSString *text = @""; NSString *text = @"";
NSString *_Nullable fileTypeForSnippet = [self fileTypeForSnippet]; NSString *_Nullable fileTypeForSnippet = [self fileTypeForSnippet];
NSString *_Nullable sourceFilename = [self.quotedMessage.sourceFilename filterStringForDisplay]; NSString *_Nullable sourceFilename = [self.quotedMessage.firstThumbnailAttachment.sourceFilename filterStringForDisplay];
if (self.displayableQuotedText.displayText.length > 0) { if (self.displayableQuotedText.displayText.length > 0) {
text = self.displayableQuotedText.displayText; text = self.displayableQuotedText.displayText;

@ -43,7 +43,7 @@ class QuotedReplyPreview: UIView {
bodyLabel.font = .ows_footnote bodyLabel.font = .ows_footnote
bodyLabel.text = { bodyLabel.text = {
if let contentType = quotedMessage.contentType { if let contentType = quotedMessage.contentType() {
let emoji = TSAttachmentStream.emoji(forMimeType: contentType) let emoji = TSAttachmentStream.emoji(forMimeType: contentType)
return "\(emoji) \(quotedMessage.body ?? "")" return "\(emoji) \(quotedMessage.body ?? "")"
} else { } else {
@ -52,16 +52,15 @@ class QuotedReplyPreview: UIView {
}() }()
let thumbnailView: UIView? = { let thumbnailView: UIView? = {
// FIXME TODO if let image = quotedMessage.thumbnailImage() {
// if let image = quotedMessage.thumbnailImage() { let imageView = UIImageView(image: image)
// let imageView = UIImageView(image: image) imageView.contentMode = .scaleAspectFill
// imageView.contentMode = .scaleAspectFill imageView.autoPinToSquareAspectRatio()
// imageView.autoPinToSquareAspectRatio() imageView.layer.cornerRadius = 3.0
// imageView.layer.cornerRadius = 3.0 imageView.clipsToBounds = true
// imageView.clipsToBounds = true
// return imageView
// return imageView }
// }
return nil return nil
}() }()

@ -79,9 +79,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSAttachmentsProcessor *attachmentsProcessor = OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:transcript.attachmentPointerProtos [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:transcript.attachmentPointerProtos
timestamp:transcript.timestamp
relay:transcript.relay relay:transcript.relay
thread:thread
networkManager:self.networkManager networkManager:self.networkManager
primaryStorage:self.primaryStorage primaryStorage:self.primaryStorage
transaction:transaction]; transaction:transaction];

@ -25,14 +25,13 @@ extern NSString *const kAttachmentDownloadAttachmentIDKey;
@property (nullable, nonatomic, readonly) NSArray<NSString *> *attachmentIds; @property (nullable, nonatomic, readonly) NSArray<NSString *> *attachmentIds;
@property (nonatomic, readonly) NSArray<NSString *> *supportedAttachmentIds; @property (nonatomic, readonly) NSArray<NSString *> *supportedAttachmentIds;
@property (nonatomic, readonly) NSArray<TSAttachmentPointer *> *supportedAttachmentPointers;
@property (nonatomic, readonly) BOOL hasSupportedAttachments; @property (nonatomic, readonly) BOOL hasSupportedAttachments;
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithAttachmentProtos:(NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentProtos - (instancetype)initWithAttachmentProtos:(NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentProtos
timestamp:(uint64_t)timestamp
relay:(nullable NSString *)relay relay:(nullable NSString *)relay
thread:(TSThread *)thread
networkManager:(TSNetworkManager *)networkManager networkManager:(TSNetworkManager *)networkManager
primaryStorage:(OWSPrimaryStorage *)primaryStorage primaryStorage:(OWSPrimaryStorage *)primaryStorage
transaction:(YapDatabaseReadWriteTransaction *)transaction NS_DESIGNATED_INITIALIZER; transaction:(YapDatabaseReadWriteTransaction *)transaction NS_DESIGNATED_INITIALIZER;

@ -36,7 +36,6 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
@property (nonatomic, readonly) TSNetworkManager *networkManager; @property (nonatomic, readonly) TSNetworkManager *networkManager;
@property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage; @property (nonatomic, readonly) OWSPrimaryStorage *primaryStorage;
@property (nonatomic, readonly) NSArray<TSAttachmentPointer *> *supportedAttachmentPointers;
@end @end
@ -61,9 +60,7 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
} }
- (instancetype)initWithAttachmentProtos:(NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentProtos - (instancetype)initWithAttachmentProtos:(NSArray<OWSSignalServiceProtosAttachmentPointer *> *)attachmentProtos
timestamp:(uint64_t)timestamp
relay:(nullable NSString *)relay relay:(nullable NSString *)relay
thread:(TSThread *)thread
networkManager:(TSNetworkManager *)networkManager networkManager:(TSNetworkManager *)networkManager
primaryStorage:(OWSPrimaryStorage *)primaryStorage primaryStorage:(OWSPrimaryStorage *)primaryStorage
transaction:(YapDatabaseReadWriteTransaction *)transaction transaction:(YapDatabaseReadWriteTransaction *)transaction
@ -120,6 +117,8 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
return self; return self;
} }
// Remove this?
- (void)fetchAttachmentsForMessage:(nullable TSMessage *)message - (void)fetchAttachmentsForMessage:(nullable TSMessage *)message
primaryStorage:(OWSPrimaryStorage *)primaryStorage primaryStorage:(OWSPrimaryStorage *)primaryStorage
success:(void (^)(TSAttachmentStream *attachmentStream))successHandler success:(void (^)(TSAttachmentStream *attachmentStream))successHandler

@ -370,7 +370,7 @@ NS_ASSUME_NONNULL_BEGIN
if (![[NSFileManager defaultManager] fileExistsAtPath:self.mediaURL.path]) { if (![[NSFileManager defaultManager] fileExistsAtPath:self.mediaURL.path]) {
DDLogError(@"%@ while generating thumbnail, source file doesn't exist: %@", self.logTag, self.mediaURL); DDLogError(@"%@ while generating thumbnail, source file doesn't exist: %@", self.logTag, self.mediaURL);
// If we're not lazy-restoring this message, the attachment should exist on disk. // If we're not lazy-restoring this message, the attachment should exist on disk.
OWSAssert(self.lazyRestoreFragmentId); // OWSAssert(self.lazyRestoreFragmentId);
return; return;
} }

@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
} }
OWSSignalServiceProtosAttachmentPointer *attachmentProto = OWSSignalServiceProtosAttachmentPointer *attachmentProto =
[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0] filename:nil]; [self buildProtoForAttachmentId:self.attachmentIds[0] filename:nil];
OWSSignalServiceProtosSyncMessageContactsBuilder *contactsBuilder = OWSSignalServiceProtosSyncMessageContactsBuilder *contactsBuilder =
[OWSSignalServiceProtosSyncMessageContactsBuilder new]; [OWSSignalServiceProtosSyncMessageContactsBuilder new];

@ -35,7 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
(unsigned long)self.attachmentIds.count); (unsigned long)self.attachmentIds.count);
} }
OWSSignalServiceProtosAttachmentPointer *attachmentProto = OWSSignalServiceProtosAttachmentPointer *attachmentProto =
[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0] filename:nil]; [self buildProtoForAttachmentId:self.attachmentIds[0] filename:nil];
OWSSignalServiceProtosSyncMessageGroupsBuilder *groupsBuilder = OWSSignalServiceProtosSyncMessageGroupsBuilder *groupsBuilder =
[OWSSignalServiceProtosSyncMessageGroupsBuilder new]; [OWSSignalServiceProtosSyncMessageGroupsBuilder new];

@ -132,8 +132,8 @@ typedef NS_ENUM(NSInteger, TSGroupMetaMessage) {
* @return * @return
* An attachment pointer protobuf suitable for including in various container protobuf builders * An attachment pointer protobuf suitable for including in various container protobuf builders
*/ */
- (OWSSignalServiceProtosAttachmentPointer *)buildAttachmentProtoForAttachmentId:(NSString *)attachmentId - (OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentId:(NSString *)attachmentId
filename:(nullable NSString *)filename; filename:(nullable NSString *)filename;
- (BOOL)shouldBeSaved; - (BOOL)shouldBeSaved;

@ -415,8 +415,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
case TSGroupMessageNew: { case TSGroupMessageNew: {
if (gThread.groupModel.groupImage != nil && self.attachmentIds.count == 1) { if (gThread.groupModel.groupImage != nil && self.attachmentIds.count == 1) {
attachmentWasGroupAvatar = YES; attachmentWasGroupAvatar = YES;
[groupBuilder [groupBuilder setAvatar:[self buildProtoForAttachmentId:self.attachmentIds[0] filename:nil]];
setAvatar:[self buildAttachmentProtoForAttachmentId:self.attachmentIds[0] filename:nil]];
} }
[groupBuilder setMembersArray:gThread.groupModel.groupMemberIds]; [groupBuilder setMembersArray:gThread.groupModel.groupMemberIds];
@ -437,7 +436,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
NSMutableArray *attachments = [NSMutableArray new]; NSMutableArray *attachments = [NSMutableArray new];
for (NSString *attachmentId in self.attachmentIds) { for (NSString *attachmentId in self.attachmentIds) {
NSString *_Nullable sourceFilename = self.attachmentFilenameMap[attachmentId]; NSString *_Nullable sourceFilename = self.attachmentFilenameMap[attachmentId];
[attachments addObject:[self buildAttachmentProtoForAttachmentId:attachmentId filename:sourceFilename]]; [attachments addObject:[self buildProtoForAttachmentId:attachmentId filename:sourceFilename]];
} }
[builder setAttachmentsArray:attachments]; [builder setAttachmentsArray:attachments];
} }
@ -456,14 +455,15 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
[quoteBuilder setText:quotedMessage.body]; [quoteBuilder setText:quotedMessage.body];
} }
if (quotedMessage.thumbnailAttachmentIds.count > 0) { if (quotedMessage.attachmentInfos) {
NSMutableArray *thumbnailAttachments = [NSMutableArray new]; NSMutableArray *thumbnailAttachments = [NSMutableArray new];
for (NSString *attachmentId in quotedMessage.thumbnailAttachmentIds) { // FIXME TODO if has thumbnail build proto for attachment stream
hasQuotedAttachment = YES; // but if no thumbnail we only set contentType/filename
NSString *_Nullable sourceFilename = quotedMessage.thumbnailAttachmentFilenameMap[attachmentId]; // for (TSAttachmentStream *attachment in quotedMessage.thumbnailAttachments) {
[thumbnailAttachments addObject:[self buildAttachmentProtoForAttachmentId:attachmentId filename:sourceFilename]]; // OWSAssert([attachment isKindOfClass:[TSAttachmentStream class]]);
} //
[quoteBuilder setAttachmentsArray:thumbnailAttachments]; // [quoteBuilder addAttachments:[self buildProtoForAttachmentStream:attachment filename:attachment.sourceFilename]];]
// }
} }
if (hasQuotedText || hasQuotedAttachment) { if (hasQuotedText || hasQuotedAttachment) {
@ -498,8 +498,8 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
return !self.hasSyncedTranscript; return !self.hasSyncedTranscript;
} }
- (OWSSignalServiceProtosAttachmentPointer *)buildAttachmentProtoForAttachmentId:(NSString *)attachmentId - (OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentId:(NSString *)attachmentId
filename:(nullable NSString *)filename filename:(nullable NSString *)filename
{ {
OWSAssert(attachmentId.length > 0); OWSAssert(attachmentId.length > 0);
@ -509,7 +509,12 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
return nil; return nil;
} }
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment; TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
return [self buildProtoForAttachmentStream:attachmentStream filename:filename];
}
- (OWSSignalServiceProtosAttachmentPointer *)buildProtoForAttachmentStream:(TSAttachmentStream *)attachmentStream
filename:(nullable NSString *)filename
{
OWSSignalServiceProtosAttachmentPointerBuilder *builder = [OWSSignalServiceProtosAttachmentPointerBuilder new]; OWSSignalServiceProtosAttachmentPointerBuilder *builder = [OWSSignalServiceProtosAttachmentPointerBuilder new];
[builder setId:attachmentStream.serverId]; [builder setId:attachmentStream.serverId];
OWSAssert(attachmentStream.contentType.length > 0); OWSAssert(attachmentStream.contentType.length > 0);

@ -3,10 +3,23 @@
// //
#import <SignalServiceKit/TSYapDatabaseObject.h> #import <SignalServiceKit/TSYapDatabaseObject.h>
#import <Mantle/MTLModel.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@class TSAttachment; @class TSAttachment;
@class TSAttachmentStream;
@interface OWSAttachmentInfo: MTLModel
@property (nonatomic, readonly, nullable) NSString *contentType;
@property (nonatomic, readonly, nullable) NSString *sourceFilename;
@property (nonatomic, readonly, nullable) NSString *attachmentId;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithAttachment:(TSAttachment *)attachment;
@end
@interface TSQuotedMessage : TSYapDatabaseObject @interface TSQuotedMessage : TSYapDatabaseObject
@ -17,31 +30,32 @@ NS_ASSUME_NONNULL_BEGIN
// or attachment with caption. // or attachment with caption.
@property (nullable, nonatomic, readonly) NSString *body; @property (nullable, nonatomic, readonly) NSString *body;
//// This property can be set IFF we are quoting an attachment message, but it is optional. #pragma mark - Attachments
//@property (nullable, nonatomic, readonly) NSData *thumbnailData;
// This is a MIME type. // This is a MIME type.
// //
// This property should be set IFF we are quoting an attachment message. // This property should be set IFF we are quoting an attachment message.
@property (nullable, nonatomic, readonly) NSString *contentType; - (nullable NSString *)contentType;
- (nullable NSString *)sourceFilename;
- (instancetype)init NS_UNAVAILABLE; @property (atomic, readonly) NSArray<OWSAttachmentInfo *> *attachmentInfos;
- (void)addAttachment:(TSAttachmentStream *)attachment;
- (BOOL)hasAttachments;
- (instancetype)initWithTimestamp:(uint64_t)timestamp - (nullable TSAttachment *)firstAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction;
authorId:(NSString *)authorId - (nullable UIImage *)thumbnailImageWithTransaction:(YapDatabaseReadTransaction *)transaction;
body:(NSString *_Nullable)body
sourceFilename:(NSString *_Nullable)sourceFilename
thumbnailData:(NSData *_Nullable)thumbnailData
contentType:(NSString *_Nullable)contentType;
#pragma mark - Attachments - (instancetype)init NS_UNAVAILABLE;
@property (nonatomic, readonly) NSArray<NSString *> *thumbnailAttachmentIds; - (instancetype)initOutgoingWithTimestamp:(uint64_t)timestamp
// A map of attachment id-to-"source" filename. authorId:(NSString *)authorId
@property (nonatomic, readonly) NSMutableDictionary<NSString *, NSString *> *thumbnailAttachmentFilenameMap; body:(NSString *_Nullable)body
attachment:(TSAttachmentStream *_Nullable)attachmentStream;
- (BOOL)hasThumbnailAttachments; - (instancetype)initIncomingWithTimestamp:(uint64_t)timestamp
- (nullable TSAttachment *)firstThumbnailAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction; authorId:(NSString *)authorId
body:(NSString *_Nullable)body
attachments:(NSArray<TSAttachment *> *)attachments;
@end @end

@ -4,74 +4,171 @@
#import "TSQuotedMessage.h" #import "TSQuotedMessage.h"
#import "TSAttachment.h" #import "TSAttachment.h"
#import "TSAttachmentStream.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
@implementation TSQuotedMessage @implementation OWSAttachmentInfo
- (instancetype)initWithTimestamp:(uint64_t)timestamp - (instancetype)initWithAttachment:(TSAttachment *)attachment
authorId:(NSString *)authorId
body:(NSString *_Nullable)body
sourceFilename:(NSString *_Nullable)sourceFilename
thumbnailData:(NSData *_Nullable)thumbnailData
contentType:(NSString *_Nullable)contentType
{ {
self = [super initWithUniqueId:[NSUUID UUID].UUIDString]; self = [super init];
if (!self) { if (!self) {
return self; return self;
} }
OWSAssert(attachment.uniqueId);
OWSAssert(attachment.contentType);
_attachmentId = attachment.uniqueId;
_contentType = attachment.contentType;
// maybe nil
_sourceFilename = attachment.sourceFilename;
return self;
}
@end
@interface TSQuotedMessage ()
@property (atomic) NSArray<OWSAttachmentInfo *> *thumbnailAttachments;
@end
@implementation TSQuotedMessage
- (instancetype)initOutgoingWithTimestamp:(uint64_t)timestamp
authorId:(NSString *)authorId
body:(NSString *_Nullable)body
attachment:(TSAttachmentStream *_Nullable)attachmentStream
{
return [self initWithTimestamp:timestamp authorId:authorId body:body attachments:@[ attachmentStream ]];
}
- (instancetype)initIncomingWithTimestamp:(uint64_t)timestamp
authorId:(NSString *)authorId
body:(NSString *_Nullable)body
attachments:(NSArray<TSAttachment *> *)attachments
{
return [self initWithTimestamp:timestamp authorId:authorId body:body attachments:attachments];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
authorId:(NSString *)authorId
body:(NSString *_Nullable)body
attachments:(NSArray<TSAttachment *> *)attachments
{
OWSAssert(timestamp > 0); OWSAssert(timestamp > 0);
OWSAssert(authorId.length > 0); OWSAssert(authorId.length > 0);
self = [super init];
if (!self) {
return nil;
}
_timestamp = timestamp; _timestamp = timestamp;
_authorId = authorId; _authorId = authorId;
_body = body; _body = body;
// TODO get source filename from attachment
// _sourceFilename = sourceFilename; NSMutableArray *attachmentInfos = [NSMutableArray new];
// _thumbnailData = thumbnailData; for (TSAttachment *attachment in attachments) {
_contentType = contentType; [attachmentInfos addObject:[[OWSAttachmentInfo alloc] initWithAttachment:attachment]];
}
_thumbnailAttachments = [attachmentInfos copy];
return self; return self;
} }
// TODO maybe this should live closer to the view - (nullable TSAttachment *)firstAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction
- (nullable UIImage *)thumbnailImage
{ {
// if (self.thumbnailData.length == 0) { OWSAttachmentInfo *attachmentInfo = self.firstAttachmentInfo;
// return nil; if (!attachmentInfo) {
// } return nil;
// }
// // PERF TODO cache
// return [UIImage imageWithData:self.thumbnailData];
return nil;
}
//- (void)setThumbnailAttachmentId:(NSString *)thumbnailAttachmentId return [TSAttachment fetchObjectWithUniqueID:attachmentInfo.attachmentId];
//{ }
// _thumbnailAttachmentId = thumbnailAttachmentId;
//}
//
//- (BOOL)hasThumbnailAttachment
//{
// return self.thumbnailAttachmentId.length > 0;
//}
//
- (BOOL)hasThumbnailAttachments - (nullable OWSAttachmentInfo *)firstAttachmentInfo
{ {
return self.thumbnailAttachmentIds.count > 0; return self.attachmentInfos.firstObject;
} }
- (nullable TSAttachment *)firstThumbnailAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction; - (nullable UIImage *)thumbnailImageWithTransaction:(YapDatabaseReadTransaction *)transaction
{ {
if (!self.hasThumbnailAttachments) { TSAttachmentStream *firstAttachment = (TSAttachmentStream *)self.firstThumbnailAttachment;
if (![firstAttachment isKindOfClass:[TSAttachmentStream class]]) {
return nil; return nil;
} }
return [TSAttachment fetchObjectWithUniqueID:self.thumbnailAttachmentIds.firstObject transaction:transaction]; return firstAttachment.thumbnailImage;
}
- (nullable NSString *)contentType
{
OWSAttachmentInfo *firstAttachment = self.firstThumbnailAttachment;
return firstAttachment.contentType;
} }
- (BOOL)hasThumbnailAttachments
{
return self.thumbnailAttachments.count > 0;
}
- (void)addThumbnailAttachment:(TSAttachmentStream *)attachment
{
NSMutableArray<OWSAttachmentInfo *> *existingAttachments = [self.thumbnailAttachments mutableCopy];
OWSAttachmentInfo *attachmentInfo = [[OWSAttachmentInfo alloc] initWithAttachment:attachment];
[existingAttachments addObject:attachmentInfo];
self.thumbnailAttachments = [existingAttachments copy];
}
- (nullable OWSAttachmentInfo *)firstThumbnailAttachment
{
return self.thumbnailAttachments.firstObject;
}
- (TSAttachmentStream *)thumbnailAttachmentWithTransaction:(YapDatabaseReadTransaction *)transaction
{
}
- (void)createThumbnailAttachmentIfNecessaryWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
// OWSAssert([attachment isKindOfClass:[TSAttachmentStream class]]);
// UIImage *thumbnailImage = attachment.thumbnailImage;
// // Only some media types have thumbnails
// if (thumbnailImage) {
// // Copy the thumbnail to a new attachment.
// TSAttachmentStream *thumbnailAttachment =
// [[TSAttachmentStream alloc] initWithContentType:attachment.contentType
// byteCount:attachment.byteCount
// sourceFilename:attachment.sourceFilename];
//
// NSError *error;
// NSData *_Nullable data = [attachment readDataFromFileWithError:&error];
// if (!data || error) {
// DDLogError(@"%@ Couldn't load attachment data for message sent to self: %@.", self.logTag, error);
// } else {
// [thumbnailAttachment writeData:data error:&error];
// if (error) {
// DDLogError(
// @"%@ Couldn't copy attachment data for message sent to self: %@.", self.logTag, error);
// } else {
// [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
// [thumbnailAttachment saveWithTransaction:transaction];
// quotedMessage.attachments =
// [message saveWithTransaction:transaction];
// }];
// }
// }
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -33,6 +33,8 @@
#import "OWSSyncGroupsRequestMessage.h" #import "OWSSyncGroupsRequestMessage.h"
#import "ProfileManagerProtocol.h" #import "ProfileManagerProtocol.h"
#import "TSAccountManager.h" #import "TSAccountManager.h"
#import "TSAttachment.h"
#import "TSAttachmentPointer.h"
#import "TSContactThread.h" #import "TSContactThread.h"
#import "TSDatabaseView.h" #import "TSDatabaseView.h"
#import "TSGroupModel.h" #import "TSGroupModel.h"
@ -512,9 +514,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSAssert(groupThread); OWSAssert(groupThread);
OWSAttachmentsProcessor *attachmentsProcessor = OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:@[ dataMessage.group.avatar ] [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:@[ dataMessage.group.avatar ]
timestamp:envelope.timestamp
relay:envelope.relay relay:envelope.relay
thread:groupThread
networkManager:self.networkManager networkManager:self.networkManager
primaryStorage:self.primaryStorage primaryStorage:self.primaryStorage
transaction:transaction]; transaction:transaction];
@ -552,9 +552,7 @@ NS_ASSUME_NONNULL_BEGIN
OWSAttachmentsProcessor *attachmentsProcessor = OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:dataMessage.attachments [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:dataMessage.attachments
timestamp:envelope.timestamp
relay:envelope.relay relay:envelope.relay
thread:thread
networkManager:self.networkManager networkManager:self.networkManager
primaryStorage:self.primaryStorage primaryStorage:self.primaryStorage
transaction:transaction]; transaction:transaction];
@ -993,7 +991,8 @@ NS_ASSUME_NONNULL_BEGIN
return nil; return nil;
} }
TSQuotedMessage *_Nullable quotedMessage = [self quotedMessageForDataMessage:dataMessage]; TSQuotedMessage *_Nullable quotedMessage =
[self quotedMessageForDataMessage:dataMessage envelope:envelope transaction:transaction];
DDLogDebug(@"%@ incoming message from: %@ for group: %@ with timestamp: %lu", DDLogDebug(@"%@ incoming message from: %@ for group: %@ with timestamp: %lu",
self.logTag, self.logTag,
@ -1041,7 +1040,8 @@ NS_ASSUME_NONNULL_BEGIN
transaction:transaction transaction:transaction
relay:envelope.relay]; relay:envelope.relay];
TSQuotedMessage *_Nullable quotedMessage = [self quotedMessageForDataMessage:dataMessage]; TSQuotedMessage *_Nullable quotedMessage =
[self quotedMessageForDataMessage:dataMessage envelope:envelope transaction:transaction];
TSIncomingMessage *incomingMessage = TSIncomingMessage *incomingMessage =
[[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp [[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp
@ -1063,6 +1063,8 @@ NS_ASSUME_NONNULL_BEGIN
} }
- (TSQuotedMessage *_Nullable)quotedMessageForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage - (TSQuotedMessage *_Nullable)quotedMessageForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
envelope:(OWSSignalServiceProtosEnvelope *)envelope
transaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
OWSAssert(dataMessage); OWSAssert(dataMessage);
@ -1093,22 +1095,39 @@ NS_ASSUME_NONNULL_BEGIN
hasText = YES; hasText = YES;
} }
NSString *_Nullable sourceFilename = nil; NSArray<TSAttachment *> *attachments;
NSData *_Nullable thumbnailData = nil;
NSString *_Nullable contentType = nil;
if (quoteProto.attachments.count > 0) { if (quoteProto.attachments.count == 0) {
OWSSignalServiceProtosAttachmentPointer *attachmentProto = quoteProto.attachments.firstObject; attachments = @[];
if ([attachmentProto hasContentType] && attachmentProto.contentType.length > 0) { } else {
contentType = attachmentProto.contentType; OWSAttachmentsProcessor *attachmentsProcessor =
[[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:quoteProto.attachments
if ([attachmentProto hasFileName] && attachmentProto.fileName.length > 0) { relay:envelope.relay
sourceFilename = attachmentProto.fileName; networkManager:self.networkManager
} primaryStorage:self.primaryStorage
if ([attachmentProto hasThumbnail] && attachmentProto.thumbnail.length > 0) { transaction:transaction];
thumbnailData = [attachmentProto thumbnail];
} if (!attachmentsProcessor.hasSupportedAttachments) {
attachments = @[];
} else {
attachments = attachmentsProcessor.supportedAttachmentPointers;
} }
// TODO
// [attachmentsProcessor fetchAttachmentsForMessage:nil
// transaction:transaction
// success:^(TSAttachmentStream *attachmentStream) {
// [groupThread
// updateAvatarWithAttachmentStream:attachmentStream];
// }
// failure:^(NSError *error) {
// DDLogError(@"%@ failed to fetch attachments for group
// avatar sent at: %llu. with error: %@",
// self.logTag,
// envelope.timestamp,
// error);
// }];
hasAttachment = YES; hasAttachment = YES;
} }
@ -1117,12 +1136,17 @@ NS_ASSUME_NONNULL_BEGIN
return nil; return nil;
} }
TSQuotedMessage *quotedMessage = [[TSQuotedMessage alloc] initWithTimestamp:timestamp // TSQuotedMessage *quotedMessage = [[TSQuotedMessage alloc] initIncomingWithTimestamp:timestamp
authorId:authorId // authorId:authorId
body:body // body:body
sourceFilename:sourceFilename // sourceFilename:sourceFilename
thumbnailData:thumbnailData // thumbnailData:thumbnailData
contentType:contentType]; // contentType:contentType];
TSQuotedMessage *quotedMessage = [[TSQuotedMessage alloc] initIncomingWithTimestamp:timestamp
authorId:authorId
body:body
attachments:attachments];
return quotedMessage; return quotedMessage;
} }

@ -319,124 +319,37 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
if (message.hasAttachments) { if (message.hasAttachments) {
OWSUploadOperation *uploadAttachmentOperation = OWSUploadOperation *uploadAttachmentOperation =
[[OWSUploadOperation alloc] initWithAttachmentId:message.attachmentIds.firstObject [[OWSUploadOperation alloc] initWithAttachmentId:message.attachmentIds.firstObject
message:message
dbConnection:self.dbConnection]; dbConnection:self.dbConnection];
[sendMessageOperation addDependency:uploadAttachmentOperation]; [sendMessageOperation addDependency:uploadAttachmentOperation];
[sendingQueue addOperation:uploadAttachmentOperation]; [sendingQueue addOperation:uploadAttachmentOperation];
} }
// if (message.quotedMessage.hasThumbnailAttachments) {
// OWSUploadOperation *uploadQuoteThumbnailOperation = [[OWSUploadOperation alloc] if (message.quotedMessage) {
// initWithAttachmentId:message.attachmentIds.firstObject
// message:message // TODO do we want a different thumbnail size for quotes vs the gallery? This seems reasonable,
// dbConnection:self.dbConnection]; // and has the advantage of already having been generated.
// [sendMessageOperation addDependency:uploadAttachmentOperation]; __block TSAttachmentStream *attachment;
// [sendingQueue addOperation:uploadQuoteThumbnailOperation]; [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
// } [message.quotedMessage createThumbnailAttachmentIfNecessaryWithTransaction:transaction];
attachment = (TSAttachmentStream *)[message.quotedMessage thumbnailAttachmentWithTransaction:transaction];
}];
if (attachment) {
OWSUploadOperation *uploadQuoteThumbnailOperation =
[[OWSUploadOperation alloc] initWithAttachmentId:thumbnailAttachment.uniqueId
dbConnection:self.dbConnection];
// TODO put attachment uploads on a (lowly) concurrent queue
[sendMessageOperation addDependency:uploadQuoteThumbnailOperation];
[sendingQueue addOperation:uploadQuoteThumbnailOperation];
}
}
[sendingQueue addOperation:sendMessageOperation]; [sendingQueue addOperation:sendMessageOperation];
}); });
} }
//- (void)attemptToSendMessage:(TSOutgoingMessage *)message
// success:(void (^)(void))successHandler
// failure:(RetryableFailureHandler)failureHandler
//{
// [self ensureAnyAttachmentsUploaded:message
// success:^() {
// [self sendMessageToService:message
// success:successHandler
// failure:^(NSError *error) {
// DDLogDebug(
// @"%@ Message send attempt failed: %@", self.logTag, message.debugDescription);
// failureHandler(error);
// }];
// }
// failure:^(NSError *error) {
// DDLogDebug(@"%@ Attachment upload attempt failed: %@", self.logTag, message.debugDescription);
// failureHandler(error);
// }];
//}
//- (void)ensureAnyAttachmentsUploaded:(TSOutgoingMessage *)message
// success:(void (^)(void))successHandler
// failure:(RetryableFailureHandler)failureHandler
//{
// if (!message.hasAttachments) {
// return successHandler();
// }
//
// TSAttachmentStream *attachmentStream =
// [TSAttachmentStream fetchObjectWithUniqueID:message.attachmentIds.firstObject];
//
// if (!attachmentStream) {
// OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotLoadAttachment]);
// NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
// // Not finding local attachment is a terminal failure.
// [error setIsRetryable:NO];
// return failureHandler(error);
// }
//
// [OWSUploadingService uploadAttachmentStream:attachmentStream
// message:message
// networkManager:self.networkManager
// success:successHandler
// failure:failureHandler];
//}
//- (void)ensureAnyQuotedThumbnailUploaded:(TSOutgoingMessage *)message
// success:(void (^)(void))successHandler
// failure:(RetryableFailureHandler)failureHandler
//{
// if (!message.hasAttachments) {
// return successHandler();
// }
//
// TSAttachmentStream *attachmentStream =
// [TSAttachmentStream fetchObjectWithUniqueID:message.attachmentIds.firstObject];
//
// if (!attachmentStream) {
// OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotLoadAttachment]);
// NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
// // Not finding local attachment is a terminal failure.
// [error setIsRetryable:NO];
// return failureHandler(error);
// }
//
// if (message.quotedMessage.hasThumbnailAttachment) {
// DDLogDebug(@"%@ uploading thumbnail for message: %llu", self.logTag, message.timestamp);
//
// __block TSAttachmentStream *thumbnailAttachmentStream;
// [self.dbConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
// thumbnailAttachmentStream = [message.quotedMessage thumbnailAttachmentWithTransaction:transaction];
// }];
//
// if (!thumbnailAttachmentStream) {
// OWSProdError([OWSAnalyticsEvents messageSenderErrorCouldNotLoadAttachment]);
// NSError *error = OWSErrorMakeFailedToSendOutgoingMessageError();
// // Not finding local attachment is a terminal failure.
// [error setIsRetryable:NO];
// return failureHandler(error);
// }
//
// [self.uploadingService uploadAttachmentStream:attachmentStream
// message:message
// success:^() {
// [self.uploadingService uploadAttachmentStream:attachmentStream
// message:message
// success:successHandler
// failure:failureHandler];
// }
// failure:failureHandler];
//
// }
// [self.uploadingService uploadAttachmentStream:attachmentStream
// message:message
// success:successHandler
// failure:failureHandler];
//
//
//}
- (void)enqueueTemporaryAttachment:(DataSource *)dataSource - (void)enqueueTemporaryAttachment:(DataSource *)dataSource
contentType:(NSString *)contentType contentType:(NSString *)contentType
inMessage:(TSOutgoingMessage *)message inMessage:(TSOutgoingMessage *)message

@ -141,63 +141,55 @@ NS_ASSUME_NONNULL_BEGIN
NSString *_Nullable quotedText = message.body; NSString *_Nullable quotedText = message.body;
BOOL hasText = quotedText.length > 0; BOOL hasText = quotedText.length > 0;
BOOL hasAttachment = NO; BOOL hasAttachment = NO;
NSString *_Nullable sourceFilename = nil;
NSData *_Nullable thumbnailData = nil; TSAttachment *_Nullable attachment = [message attachmentWithTransaction:transaction];
NSString *_Nullable contentType = nil; TSAttachmentStream *quotedAttachment;
if (attachment && [attachment isKindOfClass:[TSAttachmentStream class]]) {
if (message.attachmentIds.count > 0) {
NSString *attachmentId = message.attachmentIds[0]; TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
TSAttachment *_Nullable attachment =
[TSAttachment fetchObjectWithUniqueID:attachmentId transaction:transaction]; // If the attachment is "oversize text", try the quote as a reply to text, not as
if (attachment) { // a reply to an attachment.
// If the attachment is "oversize text", try to treat it appropriately. if (!hasText && [OWSMimeTypeOversizeTextMessage isEqualToString:attachment.contentType]) {
if (!hasText && [OWSMimeTypeOversizeTextMessage isEqualToString:attachment.contentType] && hasText = YES;
[attachment isKindOfClass:[TSAttachmentStream class]]) { quotedText = @"";
hasText = YES; NSData *_Nullable oversizeTextData = [NSData dataWithContentsOfFile:attachmentStream.filePath];
quotedText = @""; if (oversizeTextData) {
// We don't need to include the entire text body of the message, just
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment; // enough to render a snippet. kOversizeTextMessageSizeThreshold is our
NSData *_Nullable oversizeTextData = [NSData dataWithContentsOfFile:attachmentStream.filePath]; // limit on how long text should be in protos since they'll be stored in
if (oversizeTextData) { // the database. We apply this constant here for the same reasons.
// We don't need to include the entire text body of the message, just NSString *_Nullable oversizeText =
// enough to render a snippet. kOversizeTextMessageSizeThreshold is our [[NSString alloc] initWithData:oversizeTextData encoding:NSUTF8StringEncoding];
// limit on how long text should be in protos since they'll be stored in // First, truncate to the rough max characters.
// the database. We apply this constant here for the same reasons. NSString *_Nullable truncatedText =
NSString *_Nullable oversizeText = [oversizeText substringToIndex:kOversizeTextMessageSizeThreshold - 1];
[[NSString alloc] initWithData:oversizeTextData encoding:NSUTF8StringEncoding]; // But kOversizeTextMessageSizeThreshold is in _bytes_, not characters,
// First, truncate to the rough max characters. // so we need to continue to trim the string until it fits.
NSString *_Nullable truncatedText = while (truncatedText && truncatedText.length > 0 &&
[oversizeText substringToIndex:kOversizeTextMessageSizeThreshold - 1]; [truncatedText dataUsingEncoding:NSUTF8StringEncoding].length
// But kOversizeTextMessageSizeThreshold is in _bytes_, not characters, >= kOversizeTextMessageSizeThreshold) {
// so we need to continue to trim the string until it fits. // A very coarse binary search by halving is acceptable, since
while (truncatedText && truncatedText.length > 0 && // kOversizeTextMessageSizeThreshold is much longer than our target
[truncatedText dataUsingEncoding:NSUTF8StringEncoding].length // length of "three short lines of text on any device we might
>= kOversizeTextMessageSizeThreshold) { // display this on.
// A very coarse binary search by halving is acceptable, since //
// kOversizeTextMessageSizeThreshold is much longer than our target // The search will always converge since in the worst case (namely
// length of "three short lines of text on any device we might // a single character which in utf-8 is >= 1024 bytes) the loop will
// display this on. // exit when the string is empty.
// truncatedText = [truncatedText substringToIndex:oversizeText.length / 2];
// The search will always converge since in the worst case (namely }
// a single character which in utf-8 is >= 1024 bytes) the loop will if ([truncatedText dataUsingEncoding:NSUTF8StringEncoding].length
// exit when the string is empty. < kOversizeTextMessageSizeThreshold) {
truncatedText = [truncatedText substringToIndex:oversizeText.length / 2]; quotedText = truncatedText;
} } else {
if ([truncatedText dataUsingEncoding:NSUTF8StringEncoding].length OWSFail(@"%@ Missing valid text snippet.", self.logTag);
< kOversizeTextMessageSizeThreshold) {
quotedText = truncatedText;
} else {
OWSFail(@"%@ Missing valid text snippet.", self.logTag);
}
} }
} else {
sourceFilename = attachment.sourceFilename;
contentType = attachment.contentType;
// Try to generate a thumbnail, if possible.
thumbnailData = [self thumbnailDataForAttachment:attachment];
hasAttachment = YES;
} }
} else {
quotedAttachment = attachmentStream;
hasAttachment = YES;
} }
} }
@ -206,14 +198,10 @@ NS_ASSUME_NONNULL_BEGIN
return nil; return nil;
} }
// It's conceivable that the logic above will find neither valid text TSQuotedMessage *quotedMessage = [[TSQuotedMessage alloc] initOutgoingWithTimestamp:timestamp
// or an attachment to quote. authorId:authorId
TSQuotedMessage *quotedMessage = [[TSQuotedMessage alloc] initWithTimestamp:timestamp body:quotedText
authorId:authorId attachment:quotedAttachment];
body:quotedText
sourceFilename:sourceFilename
thumbnailData:thumbnailData
contentType:contentType];
return quotedMessage; return quotedMessage;
} }

@ -19,7 +19,6 @@ extern NSString *const kAttachmentUploadAttachmentIDKey;
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithAttachmentId:(NSString *)attachmentId - (instancetype)initWithAttachmentId:(NSString *)attachmentId
message:(TSOutgoingMessage *)outgoingMessage
dbConnection:(YapDatabaseConnection *)dbConnection NS_DESIGNATED_INITIALIZER; dbConnection:(YapDatabaseConnection *)dbConnection NS_DESIGNATED_INITIALIZER;
@end @end

@ -27,7 +27,6 @@ static const CGFloat kAttachmentUploadProgressTheta = 0.001f;
@interface OWSUploadOperation () @interface OWSUploadOperation ()
@property (readonly, nonatomic) NSString *attachmentId; @property (readonly, nonatomic) NSString *attachmentId;
@property (readonly, nonatomic) TSOutgoingMessage *outgoingMessage;
@property (readonly, nonatomic) YapDatabaseConnection *dbConnection; @property (readonly, nonatomic) YapDatabaseConnection *dbConnection;
@end @end
@ -35,7 +34,6 @@ static const CGFloat kAttachmentUploadProgressTheta = 0.001f;
@implementation OWSUploadOperation @implementation OWSUploadOperation
- (instancetype)initWithAttachmentId:(NSString *)attachmentId - (instancetype)initWithAttachmentId:(NSString *)attachmentId
message:(TSOutgoingMessage *)outgoingMessage
dbConnection:(YapDatabaseConnection *)dbConnection dbConnection:(YapDatabaseConnection *)dbConnection
{ {
self = [super init]; self = [super init];
@ -45,7 +43,6 @@ static const CGFloat kAttachmentUploadProgressTheta = 0.001f;
self.remainingRetries = 4; self.remainingRetries = 4;
_attachmentId = attachmentId; _attachmentId = attachmentId;
_outgoingMessage = outgoingMessage;
_dbConnection = dbConnection; _dbConnection = dbConnection;
return self; return self;

@ -1,10 +1,10 @@
// //
// Copyright (c) 2017 Open Whisper Systems. All rights reserved. // Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// //
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
// A protocol that abstracts away a source of NSData // A base class that abstracts away a source of NSData
// and allows us to: // and allows us to:
// //
// * Lazy-load if possible. // * Lazy-load if possible.

@ -7,6 +7,7 @@ NS_ASSUME_NONNULL_BEGIN
extern NSString *const OWSMimeTypeApplicationOctetStream; extern NSString *const OWSMimeTypeApplicationOctetStream;
extern NSString *const OWSMimeTypeApplicationZip; extern NSString *const OWSMimeTypeApplicationZip;
extern NSString *const OWSMimeTypeImagePng; extern NSString *const OWSMimeTypeImagePng;
extern NSString *const OWSMimeTypeImageJpeg;
extern NSString *const OWSMimeTypeOversizeTextMessage; extern NSString *const OWSMimeTypeOversizeTextMessage;
extern NSString *const OWSMimeTypeUnknownForTests; extern NSString *const OWSMimeTypeUnknownForTests;

@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
NSString *const OWSMimeTypeApplicationOctetStream = @"application/octet-stream"; NSString *const OWSMimeTypeApplicationOctetStream = @"application/octet-stream";
NSString *const OWSMimeTypeImagePng = @"image/png"; NSString *const OWSMimeTypeImagePng = @"image/png";
NSString *const OWSMimeTypeImageJpeg = @"image/jpeg";
NSString *const OWSMimeTypeOversizeTextMessage = @"text/x-signal-plain"; NSString *const OWSMimeTypeOversizeTextMessage = @"text/x-signal-plain";
NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype"; NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype";
NSString *const OWSMimeTypeApplicationZip = @"application/zip"; NSString *const OWSMimeTypeApplicationZip = @"application/zip";

Loading…
Cancel
Save