mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
QuotedReplyModel from SSK->SignalMessaging
// FREEBIE
This commit is contained in:
parent
1d4c0624be
commit
c56e8acc51
|
@ -327,6 +327,8 @@
|
||||||
459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */; };
|
459311FC1D75C948008DD4F0 /* OWSDeviceTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 459311FB1D75C948008DD4F0 /* OWSDeviceTableViewCell.m */; };
|
||||||
4598198E204E2F28009414F2 /* OWS108CallLoggingPreference.h in Headers */ = {isa = PBXBuildFile; fileRef = 4598198C204E2F28009414F2 /* OWS108CallLoggingPreference.h */; };
|
4598198E204E2F28009414F2 /* OWS108CallLoggingPreference.h in Headers */ = {isa = PBXBuildFile; fileRef = 4598198C204E2F28009414F2 /* OWS108CallLoggingPreference.h */; };
|
||||||
4598198F204E2F28009414F2 /* OWS108CallLoggingPreference.m in Sources */ = {isa = PBXBuildFile; fileRef = 4598198D204E2F28009414F2 /* OWS108CallLoggingPreference.m */; };
|
4598198F204E2F28009414F2 /* OWS108CallLoggingPreference.m in Sources */ = {isa = PBXBuildFile; fileRef = 4598198D204E2F28009414F2 /* OWS108CallLoggingPreference.m */; };
|
||||||
|
459B775C207BA46C0071D0AB /* OWSQuotedReplyModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 459B775A207BA3A80071D0AB /* OWSQuotedReplyModel.m */; };
|
||||||
|
459B775D207BA4810071D0AB /* OWSQuotedReplyModel.h in Headers */ = {isa = PBXBuildFile; fileRef = 459B7759207BA3A80071D0AB /* OWSQuotedReplyModel.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
45A2F005204473A3002E978A /* NewMessage.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 45A2F004204473A3002E978A /* NewMessage.aifc */; };
|
45A2F005204473A3002E978A /* NewMessage.aifc in Resources */ = {isa = PBXBuildFile; fileRef = 45A2F004204473A3002E978A /* NewMessage.aifc */; };
|
||||||
45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A663C41F92EC760027B59E /* GroupTableViewCell.swift */; };
|
45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A663C41F92EC760027B59E /* GroupTableViewCell.swift */; };
|
||||||
45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A6DAD51EBBF85500893231 /* ReminderView.swift */; };
|
45A6DAD61EBBF85500893231 /* ReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45A6DAD51EBBF85500893231 /* ReminderView.swift */; };
|
||||||
|
@ -945,6 +947,8 @@
|
||||||
4597E94F1D8313CB00040CDE /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = translations/bg.lproj/Localizable.strings; sourceTree = "<group>"; };
|
4597E94F1D8313CB00040CDE /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = translations/bg.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
4598198C204E2F28009414F2 /* OWS108CallLoggingPreference.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OWS108CallLoggingPreference.h; sourceTree = "<group>"; };
|
4598198C204E2F28009414F2 /* OWS108CallLoggingPreference.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OWS108CallLoggingPreference.h; sourceTree = "<group>"; };
|
||||||
4598198D204E2F28009414F2 /* OWS108CallLoggingPreference.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OWS108CallLoggingPreference.m; sourceTree = "<group>"; };
|
4598198D204E2F28009414F2 /* OWS108CallLoggingPreference.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OWS108CallLoggingPreference.m; sourceTree = "<group>"; };
|
||||||
|
459B7759207BA3A80071D0AB /* OWSQuotedReplyModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OWSQuotedReplyModel.h; sourceTree = "<group>"; };
|
||||||
|
459B775A207BA3A80071D0AB /* OWSQuotedReplyModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OWSQuotedReplyModel.m; sourceTree = "<group>"; };
|
||||||
45A2F004204473A3002E978A /* NewMessage.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; name = NewMessage.aifc; path = Signal/AudioFiles/NewMessage.aifc; sourceTree = SOURCE_ROOT; };
|
45A2F004204473A3002E978A /* NewMessage.aifc */ = {isa = PBXFileReference; lastKnownFileType = file; name = NewMessage.aifc; path = Signal/AudioFiles/NewMessage.aifc; sourceTree = SOURCE_ROOT; };
|
||||||
45A663C41F92EC760027B59E /* GroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupTableViewCell.swift; sourceTree = "<group>"; };
|
45A663C41F92EC760027B59E /* GroupTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
45A6DAD51EBBF85500893231 /* ReminderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReminderView.swift; sourceTree = "<group>"; };
|
45A6DAD51EBBF85500893231 /* ReminderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReminderView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1763,6 +1767,8 @@
|
||||||
45194F911FD7214600333B2C /* Models */ = {
|
45194F911FD7214600333B2C /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
459B7759207BA3A80071D0AB /* OWSQuotedReplyModel.h */,
|
||||||
|
459B775A207BA3A80071D0AB /* OWSQuotedReplyModel.m */,
|
||||||
34C42D621F4734ED0072EC04 /* OWSContactOffersInteraction.h */,
|
34C42D621F4734ED0072EC04 /* OWSContactOffersInteraction.h */,
|
||||||
34C42D631F4734ED0072EC04 /* OWSContactOffersInteraction.m */,
|
34C42D631F4734ED0072EC04 /* OWSContactOffersInteraction.m */,
|
||||||
34C42D641F4734ED0072EC04 /* TSUnreadIndicatorInteraction.h */,
|
34C42D641F4734ED0072EC04 /* TSUnreadIndicatorInteraction.h */,
|
||||||
|
@ -2305,6 +2311,7 @@
|
||||||
346129E71FD5C0C600532771 /* OWSDatabaseMigrationRunner.h in Headers */,
|
346129E71FD5C0C600532771 /* OWSDatabaseMigrationRunner.h in Headers */,
|
||||||
344D6CEA20069E070042AF96 /* SelectRecipientViewController.h in Headers */,
|
344D6CEA20069E070042AF96 /* SelectRecipientViewController.h in Headers */,
|
||||||
34480B521FD0A7A400BC14EF /* OWSLogger.h in Headers */,
|
34480B521FD0A7A400BC14EF /* OWSLogger.h in Headers */,
|
||||||
|
459B775D207BA4810071D0AB /* OWSQuotedReplyModel.h in Headers */,
|
||||||
34612A001FD5F31400532771 /* OWS105AttachmentFilePaths.h in Headers */,
|
34612A001FD5F31400532771 /* OWS105AttachmentFilePaths.h in Headers */,
|
||||||
346129F61FD5F31400532771 /* OWS103EnableVideoCalling.h in Headers */,
|
346129F61FD5F31400532771 /* OWS103EnableVideoCalling.h in Headers */,
|
||||||
344F248A20069F0600CFB4F4 /* ViewControllerUtils.h in Headers */,
|
344F248A20069F0600CFB4F4 /* ViewControllerUtils.h in Headers */,
|
||||||
|
@ -3130,6 +3137,7 @@
|
||||||
451F8A381FD7117E005CB9DA /* OWSViewController.m in Sources */,
|
451F8A381FD7117E005CB9DA /* OWSViewController.m in Sources */,
|
||||||
346129721FD1D74C00532771 /* SignalKeyingStorage.m in Sources */,
|
346129721FD1D74C00532771 /* SignalKeyingStorage.m in Sources */,
|
||||||
34480B561FD0A7A400BC14EF /* DebugLogger.m in Sources */,
|
34480B561FD0A7A400BC14EF /* DebugLogger.m in Sources */,
|
||||||
|
459B775C207BA46C0071D0AB /* OWSQuotedReplyModel.m in Sources */,
|
||||||
4551DB5A205C562300C8AE75 /* Collection+OWS.swift in Sources */,
|
4551DB5A205C562300C8AE75 /* Collection+OWS.swift in Sources */,
|
||||||
3461293C1FD1D46A00532771 /* OWSMath.m in Sources */,
|
3461293C1FD1D46A00532771 /* OWSMath.m in Sources */,
|
||||||
451F8A391FD711D6005CB9DA /* ContactsViewHelper.m in Sources */,
|
451F8A391FD711D6005CB9DA /* ContactsViewHelper.m in Sources */,
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
#import <SignalMessaging/OWSLogger.h>
|
#import <SignalMessaging/OWSLogger.h>
|
||||||
#import <SignalMessaging/OWSPreferences.h>
|
#import <SignalMessaging/OWSPreferences.h>
|
||||||
#import <SignalMessaging/OWSProfileManager.h>
|
#import <SignalMessaging/OWSProfileManager.h>
|
||||||
|
#import <SignalMessaging/OWSQuotedReplyModel.h>
|
||||||
#import <SignalMessaging/OWSSounds.h>
|
#import <SignalMessaging/OWSSounds.h>
|
||||||
#import <SignalMessaging/OWSViewController.h>
|
#import <SignalMessaging/OWSViewController.h>
|
||||||
#import <SignalMessaging/Release.h>
|
#import <SignalMessaging/Release.h>
|
||||||
|
|
|
@ -2150,7 +2150,7 @@ typedef enum : NSUInteger {
|
||||||
|
|
||||||
__block OWSQuotedReplyModel *quotedReply;
|
__block OWSQuotedReplyModel *quotedReply;
|
||||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||||
quotedReply = [OWSMessageUtils quotedReplyForMessage:message transaction:transaction];
|
quotedReply = [OWSQuotedReplyModel quotedReplyForMessage:message transaction:transaction];
|
||||||
}];
|
}];
|
||||||
|
|
||||||
if (![quotedReply isKindOfClass:[OWSQuotedReplyModel class]]) {
|
if (![quotedReply isKindOfClass:[OWSQuotedReplyModel class]]) {
|
||||||
|
|
|
@ -1969,8 +1969,8 @@ isQuotedMessageAttachmentDownloaded:(BOOL)isQuotedMessageAttachmentDownloaded
|
||||||
quotedMessage:nil
|
quotedMessage:nil
|
||||||
transaction:transaction];
|
transaction:transaction];
|
||||||
OWSAssert(messageToQuote);
|
OWSAssert(messageToQuote);
|
||||||
quotedMessage =
|
quotedMessage = [[OWSQuotedReplyModel quotedReplyForMessage:messageToQuote transaction:transaction]
|
||||||
[[OWSMessageUtils quotedReplyForMessage:messageToQuote transaction:transaction] buildQuotedMessage];
|
buildQuotedMessage];
|
||||||
} else {
|
} else {
|
||||||
TSOutgoingMessage *_Nullable messageToQuote = [self createFakeOutgoingMessage:thread
|
TSOutgoingMessage *_Nullable messageToQuote = [self createFakeOutgoingMessage:thread
|
||||||
messageBody:quotedMessageBodyWIndex
|
messageBody:quotedMessageBodyWIndex
|
||||||
|
@ -1981,8 +1981,8 @@ isQuotedMessageAttachmentDownloaded:(BOOL)isQuotedMessageAttachmentDownloaded
|
||||||
quotedMessage:nil
|
quotedMessage:nil
|
||||||
transaction:transaction];
|
transaction:transaction];
|
||||||
OWSAssert(messageToQuote);
|
OWSAssert(messageToQuote);
|
||||||
quotedMessage =
|
quotedMessage = [[OWSQuotedReplyModel quotedReplyForMessage:messageToQuote transaction:transaction]
|
||||||
[[OWSMessageUtils quotedReplyForMessage:messageToQuote transaction:transaction] buildQuotedMessage];
|
buildQuotedMessage];
|
||||||
}
|
}
|
||||||
OWSAssert(quotedMessage);
|
OWSAssert(quotedMessage);
|
||||||
|
|
||||||
|
|
48
SignalMessaging/Models/OWSQuotedReplyModel.h
Normal file
48
SignalMessaging/Models/OWSQuotedReplyModel.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
@class TSAttachmentStream;
|
||||||
|
@class TSMessage;
|
||||||
|
@class TSQuotedMessage;
|
||||||
|
@class YapDatabaseReadTransaction;
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
// View model which has already fetched any attachments.
|
||||||
|
@interface OWSQuotedReplyModel : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, readonly) uint64_t timestamp;
|
||||||
|
@property (nonatomic, readonly) NSString *authorId;
|
||||||
|
@property (nonatomic, readonly, nullable) TSAttachmentStream *attachmentStream;
|
||||||
|
|
||||||
|
// This property should be set IFF we are quoting a text message
|
||||||
|
// or attachment with caption.
|
||||||
|
@property (nullable, nonatomic, readonly) NSString *body;
|
||||||
|
|
||||||
|
#pragma mark - Attachments
|
||||||
|
|
||||||
|
// This is a MIME type.
|
||||||
|
//
|
||||||
|
// This property should be set IFF we are quoting an attachment message.
|
||||||
|
@property (nonatomic, readonly, nullable) NSString *contentType;
|
||||||
|
@property (nonatomic, readonly, nullable) NSString *sourceFilename;
|
||||||
|
@property (nonatomic, readonly, nullable) UIImage *thumbnailImage;
|
||||||
|
|
||||||
|
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
||||||
|
authorId:(NSString *)authorId
|
||||||
|
body:(NSString *_Nullable)body
|
||||||
|
attachmentStream:(nullable TSAttachmentStream *)attachment;
|
||||||
|
|
||||||
|
- (instancetype)initWithQuotedMessage:(TSQuotedMessage *)quotedMessage
|
||||||
|
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||||
|
|
||||||
|
+ (nullable instancetype)quotedReplyForMessage:(TSMessage *)message
|
||||||
|
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||||
|
|
||||||
|
- (TSQuotedMessage *)buildQuotedMessage;
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
197
SignalMessaging/Models/OWSQuotedReplyModel.m
Normal file
197
SignalMessaging/Models/OWSQuotedReplyModel.m
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "OWSQuotedReplyModel.h"
|
||||||
|
#import <SignalServiceKit/MIMETypeUtil.h>
|
||||||
|
#import <SignalServiceKit/OWSMessageSender.h>
|
||||||
|
#import <SignalServiceKit/TSAccountManager.h>
|
||||||
|
#import <SignalServiceKit/TSAttachmentStream.h>
|
||||||
|
#import <SignalServiceKit/TSIncomingMessage.h>
|
||||||
|
#import <SignalServiceKit/TSMessage.h>
|
||||||
|
#import <SignalServiceKit/TSOutgoingMessage.h>
|
||||||
|
#import <SignalServiceKit/TSQuotedMessage.h>
|
||||||
|
#import <SignalServiceKit/TSThread.h>
|
||||||
|
|
||||||
|
// View Model which has already fetched any thumbnail attachment.
|
||||||
|
@implementation OWSQuotedReplyModel
|
||||||
|
|
||||||
|
- (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) {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
_timestamp = timestamp;
|
||||||
|
_authorId = authorId;
|
||||||
|
_body = body;
|
||||||
|
_thumbnailImage = thumbnailImage;
|
||||||
|
_contentType = contentType;
|
||||||
|
_sourceFilename = sourceFilename;
|
||||||
|
|
||||||
|
// rename to originalAttachmentStream?
|
||||||
|
_attachmentStream = attachmentStream;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithQuotedMessage:(TSQuotedMessage *)quotedMessage
|
||||||
|
transaction:(YapDatabaseReadTransaction *)transaction
|
||||||
|
{
|
||||||
|
OWSAssert(quotedMessage.quotedAttachments.count <= 1);
|
||||||
|
OWSAttachmentInfo *attachmentInfo = quotedMessage.quotedAttachments.firstObject;
|
||||||
|
|
||||||
|
UIImage *_Nullable thumbnailImage;
|
||||||
|
if (attachmentInfo.thumbnailAttachmentStreamId) {
|
||||||
|
TSAttachment *attachment =
|
||||||
|
[TSAttachment fetchObjectWithUniqueID:attachmentInfo.thumbnailAttachmentStreamId 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
|
||||||
|
thumbnailImage:thumbnailImage
|
||||||
|
contentType:attachmentInfo.contentType
|
||||||
|
sourceFilename:attachmentInfo.sourceFilename
|
||||||
|
attachmentStream:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (TSQuotedMessage *)buildQuotedMessage
|
||||||
|
{
|
||||||
|
NSArray *attachments = self.attachmentStream ? @[ self.attachmentStream ] : @[];
|
||||||
|
|
||||||
|
return [[TSQuotedMessage alloc] initWithTimestamp:self.timestamp
|
||||||
|
authorId:self.authorId
|
||||||
|
body:self.body
|
||||||
|
quotedAttachmentsForSending:attachments];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (nullable OWSQuotedReplyModel *)quotedReplyForMessage:(TSMessage *)message
|
||||||
|
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||||
|
{
|
||||||
|
OWSAssert(message);
|
||||||
|
OWSAssert(transaction);
|
||||||
|
|
||||||
|
TSThread *thread = [message threadWithTransaction:transaction];
|
||||||
|
|
||||||
|
NSString *_Nullable authorId = ^{
|
||||||
|
if ([message isKindOfClass:[TSOutgoingMessage class]]) {
|
||||||
|
return [TSAccountManager localNumber];
|
||||||
|
} else if ([message isKindOfClass:[TSIncomingMessage class]]) {
|
||||||
|
return [(TSIncomingMessage *)message authorId];
|
||||||
|
} else {
|
||||||
|
OWSFail(@"%@ Unexpected message type: %@", self.logTag, message.class);
|
||||||
|
return (NSString * _Nullable) nil;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
OWSAssert(authorId.length > 0);
|
||||||
|
|
||||||
|
return [self quotedReplyForMessage:message authorId:authorId thread:thread transaction:transaction];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (nullable OWSQuotedReplyModel *)quotedReplyForMessage:(TSMessage *)message
|
||||||
|
authorId:(NSString *)authorId
|
||||||
|
thread:(TSThread *)thread
|
||||||
|
transaction:(YapDatabaseReadTransaction *)transaction
|
||||||
|
{
|
||||||
|
OWSAssert(message);
|
||||||
|
OWSAssert(authorId.length > 0);
|
||||||
|
OWSAssert(thread);
|
||||||
|
OWSAssert(transaction);
|
||||||
|
|
||||||
|
uint64_t timestamp = message.timestamp;
|
||||||
|
NSString *_Nullable quotedText = message.body;
|
||||||
|
BOOL hasText = quotedText.length > 0;
|
||||||
|
BOOL hasAttachment = NO;
|
||||||
|
|
||||||
|
TSAttachment *_Nullable attachment = [message attachmentWithTransaction:transaction];
|
||||||
|
TSAttachmentStream *quotedAttachment;
|
||||||
|
if (attachment && [attachment isKindOfClass:[TSAttachmentStream class]]) {
|
||||||
|
|
||||||
|
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
||||||
|
|
||||||
|
// If the attachment is "oversize text", try the quote as a reply to text, not as
|
||||||
|
// a reply to an attachment.
|
||||||
|
if (!hasText && [OWSMimeTypeOversizeTextMessage isEqualToString:attachment.contentType]) {
|
||||||
|
hasText = YES;
|
||||||
|
quotedText = @"";
|
||||||
|
|
||||||
|
NSData *_Nullable oversizeTextData = [NSData dataWithContentsOfFile:attachmentStream.filePath];
|
||||||
|
if (oversizeTextData) {
|
||||||
|
// We don't need to include the entire text body of the message, just
|
||||||
|
// enough to render a snippet. kOversizeTextMessageSizeThreshold is our
|
||||||
|
// limit on how long text should be in protos since they'll be stored in
|
||||||
|
// the database. We apply this constant here for the same reasons.
|
||||||
|
NSString *_Nullable oversizeText =
|
||||||
|
[[NSString alloc] initWithData:oversizeTextData encoding:NSUTF8StringEncoding];
|
||||||
|
// First, truncate to the rough max characters.
|
||||||
|
NSString *_Nullable truncatedText =
|
||||||
|
[oversizeText substringToIndex:kOversizeTextMessageSizeThreshold - 1];
|
||||||
|
// But kOversizeTextMessageSizeThreshold is in _bytes_, not characters,
|
||||||
|
// so we need to continue to trim the string until it fits.
|
||||||
|
while (truncatedText && truncatedText.length > 0 &&
|
||||||
|
[truncatedText dataUsingEncoding:NSUTF8StringEncoding].length
|
||||||
|
>= kOversizeTextMessageSizeThreshold) {
|
||||||
|
// A very coarse binary search by halving is acceptable, since
|
||||||
|
// kOversizeTextMessageSizeThreshold is much longer than our target
|
||||||
|
// length of "three short lines of text on any device we might
|
||||||
|
// display this on.
|
||||||
|
//
|
||||||
|
// The search will always converge since in the worst case (namely
|
||||||
|
// a single character which in utf-8 is >= 1024 bytes) the loop will
|
||||||
|
// exit when the string is empty.
|
||||||
|
truncatedText = [truncatedText substringToIndex:oversizeText.length / 2];
|
||||||
|
}
|
||||||
|
if ([truncatedText dataUsingEncoding:NSUTF8StringEncoding].length < kOversizeTextMessageSizeThreshold) {
|
||||||
|
quotedText = truncatedText;
|
||||||
|
} else {
|
||||||
|
OWSFail(@"%@ Missing valid text snippet.", self.logTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quotedAttachment = attachmentStream;
|
||||||
|
hasAttachment = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasText && !hasAttachment) {
|
||||||
|
OWSFail(@"%@ quoted message has neither text nor attachment", self.logTag);
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[OWSQuotedReplyModel alloc] initWithTimestamp:timestamp
|
||||||
|
authorId:authorId
|
||||||
|
body:quotedText
|
||||||
|
attachmentStream:quotedAttachment];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
|
@ -5,6 +5,7 @@
|
||||||
#import "ThreadUtil.h"
|
#import "ThreadUtil.h"
|
||||||
#import "OWSContactOffersInteraction.h"
|
#import "OWSContactOffersInteraction.h"
|
||||||
#import "OWSContactsManager.h"
|
#import "OWSContactsManager.h"
|
||||||
|
#import "OWSQuotedReplyModel.h"
|
||||||
#import "TSUnreadIndicatorInteraction.h"
|
#import "TSUnreadIndicatorInteraction.h"
|
||||||
#import <SignalMessaging/OWSProfileManager.h>
|
#import <SignalMessaging/OWSProfileManager.h>
|
||||||
#import <SignalMessaging/SignalMessaging-Swift.h>
|
#import <SignalMessaging/SignalMessaging-Swift.h>
|
||||||
|
|
|
@ -15,38 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
@class TSThread;
|
@class TSThread;
|
||||||
@class YapDatabaseReadWriteTransaction;
|
@class YapDatabaseReadWriteTransaction;
|
||||||
|
|
||||||
// View model which has already fetched any attachments.
|
|
||||||
@interface OWSQuotedReplyModel : NSObject
|
|
||||||
|
|
||||||
@property (nonatomic, readonly) uint64_t timestamp;
|
|
||||||
@property (nonatomic, readonly) NSString *authorId;
|
|
||||||
@property (nonatomic, readonly, nullable) TSAttachmentStream *attachmentStream;
|
|
||||||
|
|
||||||
// This property should be set IFF we are quoting a text message
|
|
||||||
// or attachment with caption.
|
|
||||||
@property (nullable, nonatomic, readonly) NSString *body;
|
|
||||||
|
|
||||||
#pragma mark - Attachments
|
|
||||||
|
|
||||||
// This is a MIME type.
|
|
||||||
//
|
|
||||||
// This property should be set IFF we are quoting an attachment message.
|
|
||||||
@property (nonatomic, readonly, nullable) NSString *contentType;
|
|
||||||
@property (nonatomic, readonly, nullable) NSString *sourceFilename;
|
|
||||||
@property (nonatomic, readonly, nullable) UIImage *thumbnailImage;
|
|
||||||
|
|
||||||
- (instancetype)initWithTimestamp:(uint64_t)timestamp
|
|
||||||
authorId:(NSString *)authorId
|
|
||||||
body:(NSString *_Nullable)body
|
|
||||||
attachmentStream:(nullable TSAttachmentStream *)attachment;
|
|
||||||
|
|
||||||
- (instancetype)initWithQuotedMessage:(TSQuotedMessage *)quotedMessage
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
|
||||||
|
|
||||||
- (TSQuotedMessage *)buildQuotedMessage;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface OWSAttachmentInfo: MTLModel
|
@interface OWSAttachmentInfo: MTLModel
|
||||||
|
|
||||||
@property (nonatomic, readonly, nullable) NSString *contentType;
|
@property (nonatomic, readonly, nullable) NSString *contentType;
|
||||||
|
@ -56,8 +24,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
// to reference the original attachment when generating a thumbnail.
|
// to reference the original attachment when generating a thumbnail.
|
||||||
// We don't want to do this until the message is saved, when the user sends
|
// We don't want to do this until the message is saved, when the user sends
|
||||||
// the message so as not to end up with an orphaned file.
|
// the message so as not to end up with an orphaned file.
|
||||||
//
|
|
||||||
// TODO: rename to pendingAttachmentId or maybe pendingAttachmentStream?
|
|
||||||
@property (nonatomic, readonly, nullable) NSString *attachmentId;
|
@property (nonatomic, readonly, nullable) NSString *attachmentId;
|
||||||
|
|
||||||
// References a yet-to-be downloaded thumbnail file
|
// References a yet-to-be downloaded thumbnail file
|
||||||
|
@ -76,8 +42,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
// TODO make this a MantleModel not a YapDatabaseObject.
|
|
||||||
|
|
||||||
@interface TSQuotedMessage : MTLModel
|
@interface TSQuotedMessage : MTLModel
|
||||||
|
|
||||||
@property (nonatomic, readonly) uint64_t timestamp;
|
@property (nonatomic, readonly) uint64_t timestamp;
|
||||||
|
|
|
@ -46,89 +46,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
// View Model which has already fetched any thumbnail attachment.
|
|
||||||
@implementation OWSQuotedReplyModel
|
|
||||||
|
|
||||||
- (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) {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
_timestamp = timestamp;
|
|
||||||
_authorId = authorId;
|
|
||||||
_body = body;
|
|
||||||
_thumbnailImage = thumbnailImage;
|
|
||||||
_contentType = contentType;
|
|
||||||
_sourceFilename = sourceFilename;
|
|
||||||
|
|
||||||
// rename to originalAttachmentStream?
|
|
||||||
_attachmentStream = attachmentStream;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (instancetype)initWithQuotedMessage:(TSQuotedMessage *)quotedMessage
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
{
|
|
||||||
OWSAssert(quotedMessage.quotedAttachments.count <= 1);
|
|
||||||
OWSAttachmentInfo *attachmentInfo = quotedMessage.quotedAttachments.firstObject;
|
|
||||||
|
|
||||||
UIImage *_Nullable thumbnailImage;
|
|
||||||
if (attachmentInfo.thumbnailAttachmentStreamId) {
|
|
||||||
TSAttachment *attachment =
|
|
||||||
[TSAttachment fetchObjectWithUniqueID:attachmentInfo.thumbnailAttachmentStreamId 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
|
|
||||||
thumbnailImage:thumbnailImage
|
|
||||||
contentType:attachmentInfo.contentType
|
|
||||||
sourceFilename:attachmentInfo.sourceFilename
|
|
||||||
attachmentStream:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (TSQuotedMessage *)buildQuotedMessage
|
|
||||||
{
|
|
||||||
NSArray *attachments = self.attachmentStream ? @[ self.attachmentStream ] : @[];
|
|
||||||
|
|
||||||
return [[TSQuotedMessage alloc] initWithTimestamp:self.timestamp
|
|
||||||
authorId:self.authorId
|
|
||||||
body:self.body
|
|
||||||
quotedAttachmentsForSending:attachments];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface TSQuotedMessage ()
|
@interface TSQuotedMessage ()
|
||||||
|
|
||||||
@property (atomic) NSArray<OWSAttachmentInfo *> *quotedAttachments;
|
@property (atomic) NSArray<OWSAttachmentInfo *> *quotedAttachments;
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@class OWSQuotedReplyModel;
|
|
||||||
@class TSMessage;
|
@class TSMessage;
|
||||||
@class TSThread;
|
@class TSThread;
|
||||||
@class YapDatabaseReadTransaction;
|
@class YapDatabaseReadTransaction;
|
||||||
|
@ -20,9 +19,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
- (void)updateApplicationBadgeCount;
|
- (void)updateApplicationBadgeCount;
|
||||||
|
|
||||||
+ (nullable OWSQuotedReplyModel *)quotedReplyForMessage:(TSMessage *)message
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
|
@ -104,170 +104,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
return numberOfItems;
|
return numberOfItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (nullable OWSQuotedReplyModel *)quotedReplyForMessage:(TSMessage *)message
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
|
||||||
{
|
|
||||||
OWSAssert(message);
|
|
||||||
OWSAssert(transaction);
|
|
||||||
|
|
||||||
TSThread *thread = [message threadWithTransaction:transaction];
|
|
||||||
|
|
||||||
NSString *_Nullable authorId = ^{
|
|
||||||
if ([message isKindOfClass:[TSOutgoingMessage class]]) {
|
|
||||||
return [TSAccountManager localNumber];
|
|
||||||
} else if ([message isKindOfClass:[TSIncomingMessage class]]) {
|
|
||||||
return [(TSIncomingMessage *)message authorId];
|
|
||||||
} else {
|
|
||||||
OWSFail(@"%@ Unexpected message type: %@", self.logTag, message.class);
|
|
||||||
return (NSString * _Nullable) nil;
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
OWSAssert(authorId.length > 0);
|
|
||||||
|
|
||||||
return [self quotedReplyForMessage:message authorId:authorId thread:thread transaction:transaction];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (nullable OWSQuotedReplyModel *)quotedReplyForMessage:(TSMessage *)message
|
|
||||||
authorId:(NSString *)authorId
|
|
||||||
thread:(TSThread *)thread
|
|
||||||
transaction:(YapDatabaseReadTransaction *)transaction
|
|
||||||
{
|
|
||||||
OWSAssert(message);
|
|
||||||
OWSAssert(authorId.length > 0);
|
|
||||||
OWSAssert(thread);
|
|
||||||
OWSAssert(transaction);
|
|
||||||
|
|
||||||
uint64_t timestamp = message.timestamp;
|
|
||||||
NSString *_Nullable quotedText = message.body;
|
|
||||||
BOOL hasText = quotedText.length > 0;
|
|
||||||
BOOL hasAttachment = NO;
|
|
||||||
|
|
||||||
TSAttachment *_Nullable attachment = [message attachmentWithTransaction:transaction];
|
|
||||||
TSAttachmentStream *quotedAttachment;
|
|
||||||
if (attachment && [attachment isKindOfClass:[TSAttachmentStream class]]) {
|
|
||||||
|
|
||||||
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
|
||||||
|
|
||||||
// If the attachment is "oversize text", try the quote as a reply to text, not as
|
|
||||||
// a reply to an attachment.
|
|
||||||
if (!hasText && [OWSMimeTypeOversizeTextMessage isEqualToString:attachment.contentType]) {
|
|
||||||
hasText = YES;
|
|
||||||
quotedText = @"";
|
|
||||||
|
|
||||||
NSData *_Nullable oversizeTextData = [NSData dataWithContentsOfFile:attachmentStream.filePath];
|
|
||||||
if (oversizeTextData) {
|
|
||||||
// We don't need to include the entire text body of the message, just
|
|
||||||
// enough to render a snippet. kOversizeTextMessageSizeThreshold is our
|
|
||||||
// limit on how long text should be in protos since they'll be stored in
|
|
||||||
// the database. We apply this constant here for the same reasons.
|
|
||||||
NSString *_Nullable oversizeText =
|
|
||||||
[[NSString alloc] initWithData:oversizeTextData encoding:NSUTF8StringEncoding];
|
|
||||||
// First, truncate to the rough max characters.
|
|
||||||
NSString *_Nullable truncatedText =
|
|
||||||
[oversizeText substringToIndex:kOversizeTextMessageSizeThreshold - 1];
|
|
||||||
// But kOversizeTextMessageSizeThreshold is in _bytes_, not characters,
|
|
||||||
// so we need to continue to trim the string until it fits.
|
|
||||||
while (truncatedText && truncatedText.length > 0 &&
|
|
||||||
[truncatedText dataUsingEncoding:NSUTF8StringEncoding].length
|
|
||||||
>= kOversizeTextMessageSizeThreshold) {
|
|
||||||
// A very coarse binary search by halving is acceptable, since
|
|
||||||
// kOversizeTextMessageSizeThreshold is much longer than our target
|
|
||||||
// length of "three short lines of text on any device we might
|
|
||||||
// display this on.
|
|
||||||
//
|
|
||||||
// The search will always converge since in the worst case (namely
|
|
||||||
// a single character which in utf-8 is >= 1024 bytes) the loop will
|
|
||||||
// exit when the string is empty.
|
|
||||||
truncatedText = [truncatedText substringToIndex:oversizeText.length / 2];
|
|
||||||
}
|
|
||||||
if ([truncatedText dataUsingEncoding:NSUTF8StringEncoding].length
|
|
||||||
< kOversizeTextMessageSizeThreshold) {
|
|
||||||
quotedText = truncatedText;
|
|
||||||
} else {
|
|
||||||
OWSFail(@"%@ Missing valid text snippet.", self.logTag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quotedAttachment = attachmentStream;
|
|
||||||
hasAttachment = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasText && !hasAttachment) {
|
|
||||||
OWSFail(@"%@ quoted message has neither text nor attachment", self.logTag);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [[OWSQuotedReplyModel alloc] initWithTimestamp:timestamp
|
|
||||||
authorId:authorId
|
|
||||||
body:quotedText
|
|
||||||
attachmentStream:quotedAttachment];
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (nullable NSData *)thumbnailDataForAttachment:(TSAttachment *)attachment
|
|
||||||
{
|
|
||||||
OWSAssert(attachment);
|
|
||||||
|
|
||||||
// Try to generate a thumbnail, if possible.
|
|
||||||
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
|
||||||
UIImage *_Nullable attachmentImage = [attachmentStream image];
|
|
||||||
if (!attachmentImage) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
CGSize attachmentImageSizePx;
|
|
||||||
attachmentImageSizePx.width = CGImageGetWidth(attachmentImage.CGImage);
|
|
||||||
attachmentImageSizePx.height = CGImageGetHeight(attachmentImage.CGImage);
|
|
||||||
if (attachmentImageSizePx.width <= 0 || attachmentImageSizePx.height <= 0) {
|
|
||||||
DDLogError(@"%@ attachment thumbnail has invalid size.", self.logTag);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Revisit this value.
|
|
||||||
const int kMaxThumbnailSizePx = 100;
|
|
||||||
|
|
||||||
// Try to resize image to thumbnail if necessary.
|
|
||||||
if (attachmentImageSizePx.width > kMaxThumbnailSizePx || attachmentImageSizePx.height > kMaxThumbnailSizePx) {
|
|
||||||
const CGFloat widthFactor = kMaxThumbnailSizePx / attachmentImageSizePx.width;
|
|
||||||
const CGFloat heightFactor = kMaxThumbnailSizePx / attachmentImageSizePx.height;
|
|
||||||
const CGFloat scalingFactor = MIN(widthFactor, heightFactor);
|
|
||||||
const CGFloat scaledWidthPx = (CGFloat)round(attachmentImageSizePx.width * scalingFactor);
|
|
||||||
const CGFloat scaledHeightPx = (CGFloat)round(attachmentImageSizePx.height * scalingFactor);
|
|
||||||
|
|
||||||
if (scaledWidthPx <= 0 || scaledHeightPx <= 0) {
|
|
||||||
DDLogError(@"%@ can't determined desired size for attachment thumbnail.", self.logTag);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scaledWidthPx > 0 && scaledHeightPx > 0) {
|
|
||||||
attachmentImage = [attachmentImage resizedImageToSize:CGSizeMake(scaledWidthPx, scaledHeightPx)];
|
|
||||||
if (!attachmentImage) {
|
|
||||||
DDLogError(@"%@ attachment thumbnail could not be resized.", self.logTag);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
attachmentImageSizePx.width = CGImageGetWidth(attachmentImage.CGImage);
|
|
||||||
attachmentImageSizePx.height = CGImageGetHeight(attachmentImage.CGImage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attachmentImageSizePx.width <= 0 || attachmentImageSizePx.height <= 0) {
|
|
||||||
DDLogError(@"%@ resized attachment thumbnail has invalid size.", self.logTag);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSData *_Nullable attachmentImageData = UIImagePNGRepresentation(attachmentImage);
|
|
||||||
if (!attachmentImage) {
|
|
||||||
OWSFail(@"%@ attachment thumbnail could not be written to PNG.", self.logTag);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
return attachmentImageData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
Loading…
Reference in a new issue