Generate thumbnail when quoted attachment is available locally

// FREEBIE
This commit is contained in:
Michael Kirk 2018-04-06 20:08:17 -04:00
parent 351f9ea263
commit 42f454b075
8 changed files with 130 additions and 55 deletions

View File

@ -77,6 +77,8 @@ NS_ASSUME_NONNULL_BEGIN
// Marks attachment as having completed "lazy backup restore."
- (void)updateWithLazyRestoreComplete;
- (nullable TSAttachmentStream *)cloneAsThumbnail;
@end
NS_ASSUME_NONNULL_END

View File

@ -677,6 +677,31 @@ NS_ASSUME_NONNULL_BEGIN
}];
}
- (nullable TSAttachmentStream *)cloneAsThumbnail
{
NSData *thumbnailData = self.thumbnailData;
// Only some media types have thumbnails
if (!thumbnailData) {
return nil;
}
// Copy the thumbnail to a new attachment.
NSString *thumbnailName = [NSString stringWithFormat:@"quoted-thumbnail-%@", self.sourceFilename];
TSAttachmentStream *thumbnailAttachment =
[[TSAttachmentStream alloc] initWithContentType:@"image/jpeg"
byteCount:(uint32_t)thumbnailData.length
sourceFilename:thumbnailName];
NSError *error;
BOOL success = [thumbnailAttachment writeData:thumbnailData error:&error];
if (!success || error) {
DDLogError(@"%@ Couldn't copy attachment data for message sent to self: %@.", self.logTag, error);
return nil;
}
return thumbnailAttachment;
}
@end
NS_ASSUME_NONNULL_END

View File

@ -93,6 +93,7 @@ NS_ASSUME_NONNULL_BEGIN
return foundMessage;
}
// TODO get rid of this method and instead populate authorId in initWithCoder:
- (NSString *)messageAuthorId
{
// authorId isn't set on all legacy messages, so we take

View File

@ -42,6 +42,9 @@ typedef NS_ENUM(NSInteger, OWSInteractionType) {
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
ofClass:(Class)clazz
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
+ (NSArray<TSInteraction *> *)interactionsWithTimestamp:(uint64_t)timestamp
filter:(BOOL (^_Nonnull)(TSInteraction *))filter
withTransaction:(YapDatabaseReadWriteTransaction *)transaction;
- (NSDate *)dateForSorting;
- (uint64_t)timestampForSorting;

View File

@ -69,7 +69,9 @@ NS_ASSUME_NONNULL_BEGIN
@end
@interface TSQuotedMessage : TSYapDatabaseObject
// TODO make this a MantleModel not a YapDatabaseObject.
@interface TSQuotedMessage : MTLModel
@property (nonatomic, readonly) uint64_t timestamp;
@property (nonatomic, readonly) NSString *authorId;
@ -109,7 +111,6 @@ NS_ASSUME_NONNULL_BEGIN
body:(NSString *_Nullable)body
quotedAttachmentsForSending:(NSArray<TSAttachment *> *)attachments;
@end
#pragma mark -

View File

@ -42,28 +42,28 @@ NS_ASSUME_NONNULL_BEGIN
// View Model which has already fetched any thumbnail attachment.
@implementation OWSQuotedReplyModel
// This is a MIME type.
//
// This property should be set IFF we are quoting an attachment message.
- (nullable NSString *)contentType
{
return self.attachmentStream.contentType;
}
- (nullable NSString *)sourceFilename
{
return self.attachmentStream.sourceFilename;
}
- (nullable UIImage *)thumbnailImage
{
return self.attachmentStream.thumbnailImage;
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
authorId:(NSString *)authorId
body:(NSString *_Nullable)body
attachmentStream:(nullable TSAttachmentStream *)attachmentStream
{
return [self initWithTimestamp:timestamp
authorId:authorId
body:body
thumbnailImage:attachmentStream.thumbnailImage
contentType:attachmentStream.contentType
sourceFilename:attachmentStream.sourceFilename
attachmentStream:attachmentStream];
}
- (instancetype)initWithTimestamp:(uint64_t)timestamp
authorId:(NSString *)authorId
body:(nullable NSString *)body
thumbnailImage:(nullable UIImage *)thumbnailImage
contentType:(nullable NSString *)contentType
sourceFilename:(nullable NSString *)sourceFilename
attachmentStream:(nullable TSAttachmentStream *)attachmentStream
{
self = [super init];
if (!self) {
@ -73,6 +73,11 @@ NS_ASSUME_NONNULL_BEGIN
_timestamp = timestamp;
_authorId = authorId;
_body = body;
_thumbnailImage = thumbnailImage;
_contentType = contentType;
_sourceFilename = sourceFilename;
// rename to originalAttachmentStream?
_attachmentStream = attachmentStream;
return self;
@ -81,19 +86,28 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithQuotedMessage:(TSQuotedMessage *)quotedMessage
transaction:(YapDatabaseReadTransaction *)transaction
{
TSAttachment *attachment =
[TSAttachment fetchObjectWithUniqueID:quotedMessage.quotedAttachments.firstObject.attachmentId
transaction:transaction];
OWSAssert(quotedMessage.quotedAttachments.count <= 1);
OWSAttachmentInfo *attachmentInfo = quotedMessage.quotedAttachments.firstObject;
TSAttachmentStream *attachmentStream;
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
attachmentStream = (TSAttachmentStream *)attachment;
UIImage *_Nullable thumbnailImage;
if (attachmentInfo.thumbnailAttachmentId) {
TSAttachment *attachment =
[TSAttachment fetchObjectWithUniqueID:attachmentInfo.thumbnailAttachmentId transaction:transaction];
TSAttachmentStream *attachmentStream;
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
attachmentStream = (TSAttachmentStream *)attachment;
thumbnailImage = attachmentStream.image;
}
}
return [self initWithTimestamp:quotedMessage.timestamp
authorId:quotedMessage.authorId
body:quotedMessage.body
attachmentStream:attachmentStream];
thumbnailImage:thumbnailImage
contentType:attachmentInfo.contentType
sourceFilename:attachmentInfo.sourceFilename
attachmentStream:nil];
}
- (TSQuotedMessage *)buildQuotedMessage
@ -253,38 +267,21 @@ NS_ASSUME_NONNULL_BEGIN
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
continue;
}
TSAttachmentStream *sourceStream = (TSAttachmentStream *)attachment;
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
NSData *thumbnailData = attachmentStream.thumbnailData;
// Only some media types have thumbnails
if (thumbnailData) {
// Copy the thumbnail to a new attachment.
NSString *thumbnailName =
[NSString stringWithFormat:@"quoted-thumbnail-%@", attachmentStream.sourceFilename];
TSAttachmentStream *thumbnailAttachment =
[[TSAttachmentStream alloc] initWithContentType:@"image/jpeg"
byteCount:attachmentStream.byteCount
sourceFilename:thumbnailName];
NSError *error;
[thumbnailAttachment writeData:thumbnailData error:&error];
if (error) {
DDLogError(@"%@ Couldn't copy attachment data for message sent to self: %@.", self.logTag, error);
} else {
[thumbnailAttachment saveWithTransaction:transaction];
info.thumbnailAttachmentId = thumbnailAttachment.uniqueId;
[thumbnailAttachments addObject:thumbnailAttachment];
}
TSAttachmentStream *_Nullable thumbnailStream = [sourceStream cloneAsThumbnail];
if (!thumbnailStream) {
continue;
}
}
if (thumbnailAttachments.count > 0) {
// Save to record any self.quotedAttachments[].thumbnailAttachmentId
[self saveWithTransaction:transaction];
[thumbnailStream saveWithTransaction:transaction];
info.thumbnailAttachmentId = thumbnailStream.uniqueId;
[thumbnailAttachments addObject:thumbnailStream];
}
return [thumbnailAttachments copy];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -35,6 +35,7 @@
#import "TSAccountManager.h"
#import "TSAttachment.h"
#import "TSAttachmentPointer.h"
#import "TSAttachmentStream.h"
#import "TSContactThread.h"
#import "TSDatabaseView.h"
#import "TSGroupModel.h"
@ -990,8 +991,10 @@ NS_ASSUME_NONNULL_BEGIN
return nil;
}
TSQuotedMessage *_Nullable quotedMessage =
[self quotedMessageForDataMessage:dataMessage envelope:envelope transaction:transaction];
TSQuotedMessage *_Nullable quotedMessage = [self quotedMessageForDataMessage:dataMessage
envelope:envelope
thread:oldGroupThread
transaction:transaction];
DDLogDebug(@"%@ incoming message from: %@ for group: %@ with timestamp: %lu",
self.logTag,
@ -1038,7 +1041,7 @@ NS_ASSUME_NONNULL_BEGIN
relay:envelope.relay];
TSQuotedMessage *_Nullable quotedMessage =
[self quotedMessageForDataMessage:dataMessage envelope:envelope transaction:transaction];
[self quotedMessageForDataMessage:dataMessage envelope:envelope thread:thread transaction:transaction];
TSIncomingMessage *incomingMessage =
[[TSIncomingMessage alloc] initIncomingMessageWithTimestamp:timestamp
@ -1059,6 +1062,7 @@ NS_ASSUME_NONNULL_BEGIN
- (TSQuotedMessage *_Nullable)quotedMessageForDataMessage:(OWSSignalServiceProtosDataMessage *)dataMessage
envelope:(OWSSignalServiceProtosEnvelope *)envelope
thread:(TSThread *)thread
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(dataMessage);
@ -1097,8 +1101,46 @@ NS_ASSUME_NONNULL_BEGIN
[[OWSAttachmentInfo alloc] initWithAttachmentId:nil
contentType:quotedAttachment.contentType
sourceFilename:quotedAttachment.fileName];
TSMessage *_Nullable quotedMessage = (TSMessage *)[TSInteraction
interactionsWithTimestamp:timestamp
filter:^BOOL(TSInteraction *interaction) {
if (![thread.uniqueId isEqual:interaction.uniqueThreadId]) {
return NO;
}
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)interaction;
return [authorId isEqual:incomingMessage.messageAuthorId];
} else if ([interaction isKindOfClass:[TSOutgoingMessage class]]) {
return [authorId isEqual:[TSAccountManager localNumber]];
} else {
// ignore other interaction types
return NO;
}
}
withTransaction:transaction]
.firstObject;
// We still have the existing quoted message locally.
// Derive any thumbnail locally rather than fetching one over the network.
if (quotedMessage) {
TSAttachment *attachment = [quotedMessage attachmentWithTransaction:transaction];
if ([attachment isKindOfClass:[TSAttachmentStream class]]) {
TSAttachmentStream *sourceStream = (TSAttachmentStream *)attachment;
TSAttachmentStream *thumbnailStream = [sourceStream cloneAsThumbnail];
[thumbnailStream saveWithTransaction:transaction];
attachmentInfo.thumbnailAttachmentId = thumbnailStream.uniqueId;
}
}
[attachmentInfos addObject:attachmentInfo];
}
// TODO - but only if the attachment can't be found locally.
// OWSAttachmentsProcessor *attachmentsProcessor =
// [[OWSAttachmentsProcessor alloc] initWithAttachmentProtos:quoteProto.attachments

View File

@ -333,8 +333,12 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
thumbnailAttachments =
[message.quotedMessage createThumbnailAttachmentsIfNecessaryWithTransaction:transaction];
if (thumbnailAttachments.count > 0) {
[message touchWithTransaction:transaction];
}
}];
// Though we currently only ever expect at most one thumbnail, the proto data model
// suggests this could change. The logic is intended to work with multiple, but
// if we ever actually want to send multiple, we should do more testing.