2017-10-10 22:13:54 +02:00
|
|
|
//
|
2019-01-11 21:23:06 +01:00
|
|
|
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
2017-10-10 22:13:54 +02:00
|
|
|
//
|
|
|
|
|
2020-11-12 03:20:28 +01:00
|
|
|
#import <CoreServices/CoreServices.h>
|
2017-10-10 22:13:54 +02:00
|
|
|
#import "ConversationViewItem.h"
|
2019-05-02 23:58:48 +02:00
|
|
|
#import "Session-Swift.h"
|
2019-08-28 08:07:14 +02:00
|
|
|
#import "AnyPromise.h"
|
2020-11-11 07:45:50 +01:00
|
|
|
#import <SignalUtilitiesKit/OWSUnreadIndicator.h>
|
2020-11-23 00:24:40 +01:00
|
|
|
#import <SessionUtilitiesKit/NSData+Image.h>
|
2020-11-26 00:37:56 +01:00
|
|
|
#import <SessionUtilitiesKit/NSString+SSK.h>
|
|
|
|
#import <SessionMessagingKit/TSInteraction.h>
|
2020-11-25 06:15:16 +01:00
|
|
|
#import <SessionMessagingKit/SSKEnvironment.h>
|
2020-11-11 07:45:50 +01:00
|
|
|
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
2021-01-29 01:46:32 +01:00
|
|
|
NSString *const SNAudioDidFinishPlayingNotification = @"SNAudioDidFinishPlayingNotification";
|
|
|
|
|
2017-10-12 19:48:09 +02:00
|
|
|
NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType)
|
|
|
|
{
|
|
|
|
switch (cellType) {
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_TextOnlyMessage:
|
|
|
|
return @"OWSMessageCellType_TextOnlyMessage";
|
2017-10-12 19:48:09 +02:00
|
|
|
case OWSMessageCellType_Audio:
|
|
|
|
return @"OWSMessageCellType_Audio";
|
|
|
|
case OWSMessageCellType_GenericAttachment:
|
|
|
|
return @"OWSMessageCellType_GenericAttachment";
|
2017-12-19 19:35:39 +01:00
|
|
|
case OWSMessageCellType_Unknown:
|
|
|
|
return @"OWSMessageCellType_Unknown";
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_MediaMessage:
|
|
|
|
return @"OWSMessageCellType_MediaMessage";
|
2019-03-15 18:21:54 +01:00
|
|
|
case OWSMessageCellType_OversizeTextDownloading:
|
|
|
|
return @"OWSMessageCellType_OversizeTextDownloading";
|
2017-10-12 19:48:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-25 20:53:54 +02:00
|
|
|
#pragma mark -
|
|
|
|
|
2018-11-07 17:52:34 +01:00
|
|
|
@implementation ConversationMediaAlbumItem
|
2018-11-05 17:07:00 +01:00
|
|
|
|
|
|
|
- (instancetype)initWithAttachment:(TSAttachment *)attachment
|
|
|
|
attachmentStream:(nullable TSAttachmentStream *)attachmentStream
|
2018-11-07 16:36:53 +01:00
|
|
|
caption:(nullable NSString *)caption
|
2018-11-05 17:07:00 +01:00
|
|
|
mediaSize:(CGSize)mediaSize
|
|
|
|
{
|
|
|
|
OWSAssertDebug(attachment);
|
|
|
|
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
_attachment = attachment;
|
|
|
|
_attachmentStream = attachmentStream;
|
2018-11-07 16:36:53 +01:00
|
|
|
_caption = caption;
|
2018-11-05 17:07:00 +01:00
|
|
|
_mediaSize = mediaSize;
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
2019-01-14 22:17:59 +01:00
|
|
|
|
|
|
|
- (BOOL)isFailedDownload
|
|
|
|
{
|
|
|
|
if (![self.attachment isKindOfClass:[TSAttachmentPointer class]]) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
TSAttachmentPointer *attachmentPointer = (TSAttachmentPointer *)self.attachment;
|
|
|
|
return attachmentPointer.state == TSAttachmentPointerStateFailed;
|
|
|
|
}
|
|
|
|
|
2018-11-05 17:07:00 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
2018-09-28 00:49:01 +02:00
|
|
|
@interface ConversationInteractionViewItem ()
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
@property (nonatomic, nullable) NSValue *cachedCellSize;
|
|
|
|
|
2018-02-23 21:44:46 +01:00
|
|
|
#pragma mark - OWSAudioPlayerDelegate
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
@property (nonatomic) AudioPlaybackState audioPlaybackState;
|
|
|
|
@property (nonatomic) CGFloat audioProgressSeconds;
|
2017-11-20 20:50:43 +01:00
|
|
|
@property (nonatomic) CGFloat audioDurationSeconds;
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
#pragma mark - View State
|
|
|
|
|
|
|
|
@property (nonatomic) BOOL hasViewState;
|
|
|
|
@property (nonatomic) OWSMessageCellType messageCellType;
|
2018-03-29 17:25:19 +02:00
|
|
|
@property (nonatomic, nullable) DisplayableText *displayableBodyText;
|
2018-04-03 16:32:15 +02:00
|
|
|
@property (nonatomic, nullable) DisplayableText *displayableQuotedText;
|
2018-04-06 22:53:16 +02:00
|
|
|
@property (nonatomic, nullable) OWSQuotedReplyModel *quotedReply;
|
2017-10-10 22:13:54 +02:00
|
|
|
@property (nonatomic, nullable) TSAttachmentStream *attachmentStream;
|
|
|
|
@property (nonatomic, nullable) TSAttachmentPointer *attachmentPointer;
|
2018-05-05 02:46:00 +02:00
|
|
|
@property (nonatomic, nullable) ContactShareViewModel *contactShare;
|
2019-01-18 15:54:12 +01:00
|
|
|
@property (nonatomic, nullable) OWSLinkPreview *linkPreview;
|
|
|
|
@property (nonatomic, nullable) TSAttachment *linkPreviewAttachment;
|
2018-11-07 17:52:34 +01:00
|
|
|
@property (nonatomic, nullable) NSArray<ConversationMediaAlbumItem *> *mediaAlbumItems;
|
2018-09-26 15:19:12 +02:00
|
|
|
@property (nonatomic, nullable) NSString *systemMessageText;
|
2018-09-25 22:05:53 +02:00
|
|
|
@property (nonatomic, nullable) TSThread *incomingMessageAuthorThread;
|
2018-09-26 15:19:12 +02:00
|
|
|
@property (nonatomic, nullable) NSString *authorConversationColorName;
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
|
2018-09-28 00:49:01 +02:00
|
|
|
@implementation ConversationInteractionViewItem
|
|
|
|
|
|
|
|
@synthesize shouldShowDate = _shouldShowDate;
|
2021-01-29 01:46:32 +01:00
|
|
|
@synthesize shouldShowSenderProfilePicture = _shouldShowSenderProfilePicture;
|
2018-09-28 00:49:01 +02:00
|
|
|
@synthesize unreadIndicator = _unreadIndicator;
|
|
|
|
@synthesize didCellMediaFailToLoad = _didCellMediaFailToLoad;
|
|
|
|
@synthesize interaction = _interaction;
|
|
|
|
@synthesize isFirstInCluster = _isFirstInCluster;
|
|
|
|
@synthesize isGroupThread = _isGroupThread;
|
2021-01-29 01:46:32 +01:00
|
|
|
@synthesize isOnlyMessageInCluster = _isOnlyMessageInCluster;
|
2018-09-28 00:49:01 +02:00
|
|
|
@synthesize isLastInCluster = _isLastInCluster;
|
2021-01-29 01:46:32 +01:00
|
|
|
@synthesize wasPreviousItemInfoMessage = _wasPreviousItemInfoMessage;
|
2018-09-28 00:49:01 +02:00
|
|
|
@synthesize lastAudioMessageView = _lastAudioMessageView;
|
|
|
|
@synthesize senderName = _senderName;
|
|
|
|
@synthesize shouldHideFooter = _shouldHideFooter;
|
2017-10-10 22:13:54 +02:00
|
|
|
|
2017-11-16 17:22:16 +01:00
|
|
|
- (instancetype)initWithInteraction:(TSInteraction *)interaction
|
|
|
|
isGroupThread:(BOOL)isGroupThread
|
|
|
|
transaction:(YapDatabaseReadTransaction *)transaction
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(interaction);
|
|
|
|
OWSAssertDebug(transaction);
|
2018-06-22 19:48:23 +02:00
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
if (!self) {
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
_interaction = interaction;
|
2017-10-12 05:03:28 +02:00
|
|
|
_isGroupThread = isGroupThread;
|
2018-09-26 15:19:12 +02:00
|
|
|
|
2017-11-16 17:22:16 +01:00
|
|
|
[self ensureViewState:transaction];
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2017-11-16 17:22:16 +01:00
|
|
|
- (void)replaceInteraction:(TSInteraction *)interaction transaction:(YapDatabaseReadTransaction *)transaction
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(interaction);
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
_interaction = interaction;
|
|
|
|
|
2017-10-12 19:48:09 +02:00
|
|
|
self.hasViewState = NO;
|
|
|
|
self.messageCellType = OWSMessageCellType_Unknown;
|
2018-03-29 17:25:19 +02:00
|
|
|
self.displayableBodyText = nil;
|
2017-10-12 19:48:09 +02:00
|
|
|
self.attachmentStream = nil;
|
|
|
|
self.attachmentPointer = nil;
|
2019-01-18 15:54:12 +01:00
|
|
|
self.mediaAlbumItems = nil;
|
2018-04-03 16:32:15 +02:00
|
|
|
self.displayableQuotedText = nil;
|
2018-04-06 22:53:16 +02:00
|
|
|
self.quotedReply = nil;
|
2019-01-18 15:54:12 +01:00
|
|
|
self.contactShare = nil;
|
2018-08-09 16:47:43 +02:00
|
|
|
self.systemMessageText = nil;
|
2019-01-18 15:54:12 +01:00
|
|
|
self.authorConversationColorName = nil;
|
|
|
|
self.linkPreview = nil;
|
|
|
|
self.linkPreviewAttachment = nil;
|
2018-04-03 16:32:15 +02:00
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
[self clearCachedLayoutState];
|
2017-11-16 17:22:16 +01:00
|
|
|
|
|
|
|
[self ensureViewState:transaction];
|
2017-11-17 16:49:34 +01:00
|
|
|
}
|
|
|
|
|
2019-08-29 04:49:06 +02:00
|
|
|
- (OWSPrimaryStorage *)primaryStorage
|
|
|
|
{
|
|
|
|
return SSKEnvironment.shared.primaryStorage;
|
|
|
|
}
|
|
|
|
|
2018-10-31 15:05:24 +01:00
|
|
|
- (NSString *)itemId
|
|
|
|
{
|
|
|
|
return self.interaction.uniqueId;
|
|
|
|
}
|
|
|
|
|
2018-03-29 17:25:19 +02:00
|
|
|
- (BOOL)hasBodyText
|
2017-11-17 16:49:34 +01:00
|
|
|
{
|
2018-03-29 17:25:19 +02:00
|
|
|
return _displayableBodyText != nil;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
- (BOOL)hasQuotedText
|
|
|
|
{
|
|
|
|
return _displayableQuotedText != nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)hasQuotedAttachment
|
|
|
|
{
|
|
|
|
return self.quotedAttachmentMimetype.length > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isQuotedReply
|
|
|
|
{
|
|
|
|
return self.hasQuotedAttachment || self.hasQuotedText;
|
|
|
|
}
|
|
|
|
|
2018-07-11 21:43:25 +02:00
|
|
|
- (BOOL)isExpiringMessage
|
|
|
|
{
|
|
|
|
if (self.interaction.interactionType != OWSInteractionType_OutgoingMessage
|
|
|
|
&& self.interaction.interactionType != OWSInteractionType_IncomingMessage) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
TSMessage *message = (TSMessage *)self.interaction;
|
|
|
|
return message.isExpiringMessage;
|
|
|
|
}
|
|
|
|
|
2018-07-11 20:12:58 +02:00
|
|
|
- (BOOL)hasCellHeader
|
|
|
|
{
|
|
|
|
return self.shouldShowDate || self.unreadIndicator;
|
|
|
|
}
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
- (void)setShouldShowDate:(BOOL)shouldShowDate
|
|
|
|
{
|
|
|
|
if (_shouldShowDate == shouldShowDate) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_shouldShowDate = shouldShowDate;
|
|
|
|
|
|
|
|
[self clearCachedLayoutState];
|
|
|
|
}
|
|
|
|
|
2021-01-29 01:46:32 +01:00
|
|
|
- (void)setShouldShowSenderAvatar:(BOOL)shouldShowSenderProfilePicture
|
2017-10-12 06:16:23 +02:00
|
|
|
{
|
2021-01-29 01:46:32 +01:00
|
|
|
if (_shouldShowSenderProfilePicture == shouldShowSenderProfilePicture) {
|
2017-10-12 06:16:23 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-01-29 01:46:32 +01:00
|
|
|
_shouldShowSenderProfilePicture = shouldShowSenderProfilePicture;
|
2017-10-12 06:16:23 +02:00
|
|
|
|
|
|
|
[self clearCachedLayoutState];
|
|
|
|
}
|
|
|
|
|
2018-07-02 15:42:48 +02:00
|
|
|
- (void)setSenderName:(nullable NSAttributedString *)senderName
|
2018-03-29 17:49:02 +02:00
|
|
|
{
|
2018-06-26 22:04:09 +02:00
|
|
|
if ([NSObject isNullableObject:senderName equalTo:_senderName]) {
|
2018-03-29 17:49:02 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-26 22:04:09 +02:00
|
|
|
_senderName = senderName;
|
|
|
|
|
|
|
|
[self clearCachedLayoutState];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setShouldHideFooter:(BOOL)shouldHideFooter
|
|
|
|
{
|
|
|
|
if (_shouldHideFooter == shouldHideFooter) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_shouldHideFooter = shouldHideFooter;
|
2018-03-29 17:49:02 +02:00
|
|
|
|
|
|
|
[self clearCachedLayoutState];
|
|
|
|
}
|
|
|
|
|
2019-03-15 21:25:59 +01:00
|
|
|
- (void)setIsFirstInCluster:(BOOL)isFirstInCluster
|
|
|
|
{
|
|
|
|
if (_isFirstInCluster == isFirstInCluster) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_isFirstInCluster = isFirstInCluster;
|
|
|
|
|
|
|
|
// Although this doesn't affect layout size, the view model use
|
|
|
|
// hasCachedLayoutState to detect which cells needs to be redrawn due to changes.
|
|
|
|
[self clearCachedLayoutState];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setIsLastInCluster:(BOOL)isLastInCluster
|
|
|
|
{
|
|
|
|
if (_isLastInCluster == isLastInCluster) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_isLastInCluster = isLastInCluster;
|
|
|
|
|
|
|
|
// Although this doesn't affect layout size, the view model use
|
|
|
|
// hasCachedLayoutState to detect which cells needs to be redrawn due to changes.
|
|
|
|
[self clearCachedLayoutState];
|
|
|
|
}
|
|
|
|
|
2018-07-11 20:12:58 +02:00
|
|
|
- (void)setUnreadIndicator:(nullable OWSUnreadIndicator *)unreadIndicator
|
|
|
|
{
|
|
|
|
if ([NSObject isNullableObject:_unreadIndicator equalTo:unreadIndicator]) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_unreadIndicator = unreadIndicator;
|
|
|
|
|
|
|
|
[self clearCachedLayoutState];
|
|
|
|
}
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
- (void)clearCachedLayoutState
|
|
|
|
{
|
|
|
|
self.cachedCellSize = nil;
|
|
|
|
}
|
|
|
|
|
2018-12-10 17:59:00 +01:00
|
|
|
- (BOOL)hasCachedLayoutState {
|
|
|
|
return self.cachedCellSize != nil;
|
|
|
|
}
|
|
|
|
|
2018-11-07 18:39:40 +01:00
|
|
|
- (nullable TSAttachmentStream *)firstValidAlbumAttachment
|
2018-11-06 16:02:13 +01:00
|
|
|
{
|
2018-11-07 17:52:34 +01:00
|
|
|
OWSAssertDebug(self.mediaAlbumItems.count > 0);
|
2018-11-06 16:02:13 +01:00
|
|
|
|
|
|
|
// For now, use first valid attachment.
|
|
|
|
TSAttachmentStream *_Nullable attachmentStream = nil;
|
2018-11-07 17:52:34 +01:00
|
|
|
for (ConversationMediaAlbumItem *mediaAlbumItem in self.mediaAlbumItems) {
|
|
|
|
if (mediaAlbumItem.attachmentStream && mediaAlbumItem.attachmentStream.isValidVisualMedia) {
|
|
|
|
attachmentStream = mediaAlbumItem.attachmentStream;
|
2018-11-06 16:02:13 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return attachmentStream;
|
|
|
|
}
|
|
|
|
|
2018-02-23 21:44:46 +01:00
|
|
|
#pragma mark - OWSAudioPlayerDelegate
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
- (void)setAudioPlaybackState:(AudioPlaybackState)audioPlaybackState
|
|
|
|
{
|
|
|
|
_audioPlaybackState = audioPlaybackState;
|
|
|
|
|
2020-10-02 06:44:33 +02:00
|
|
|
BOOL isPlaying = (audioPlaybackState == AudioPlaybackState_Playing);
|
|
|
|
[self.lastAudioMessageView setIsPlaying:isPlaying];
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setAudioProgress:(CGFloat)progress duration:(CGFloat)duration
|
|
|
|
{
|
2017-12-19 17:38:25 +01:00
|
|
|
OWSAssertIsOnMainThread();
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
self.audioProgressSeconds = progress;
|
|
|
|
|
2021-01-29 01:46:32 +01:00
|
|
|
[self.lastAudioMessageView setProgress:(int)(progress)];
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
|
2020-11-26 00:37:56 +01:00
|
|
|
- (void)showInvalidAudioFileAlert
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
|
|
|
|
[OWSAlerts
|
|
|
|
showErrorAlertWithMessage:NSLocalizedString(@"INVALID_AUDIO_FILE_ALERT_ERROR_MESSAGE",
|
|
|
|
@"Message for the alert indicating that an audio file is invalid.")];
|
|
|
|
}
|
|
|
|
|
2021-01-29 01:46:32 +01:00
|
|
|
- (void)audioPlayerDidFinishPlaying:(OWSAudioPlayer *)player successfully:(BOOL)flag
|
|
|
|
{
|
|
|
|
if (!flag) { return; }
|
|
|
|
[NSNotificationCenter.defaultCenter postNotificationName:SNAudioDidFinishPlayingNotification object:nil];
|
|
|
|
}
|
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
#pragma mark - Displayable Text
|
2017-10-10 22:13:54 +02:00
|
|
|
|
2017-10-11 15:58:20 +02:00
|
|
|
// TODO: Now that we're caching the displayable text on the view items,
|
|
|
|
// I don't think we need this cache any more.
|
2018-04-03 16:32:15 +02:00
|
|
|
- (NSCache *)displayableTextCache
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
|
|
|
static NSCache *cache = nil;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
cache = [NSCache new];
|
|
|
|
// Cache the results for up to 1,000 messages.
|
|
|
|
cache.countLimit = 1000;
|
|
|
|
});
|
|
|
|
return cache;
|
|
|
|
}
|
|
|
|
|
2018-03-29 17:25:19 +02:00
|
|
|
- (DisplayableText *)displayableBodyTextForText:(NSString *)text interactionId:(NSString *)interactionId
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(text);
|
|
|
|
OWSAssertDebug(interactionId.length > 0);
|
2017-10-10 22:13:54 +02:00
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
NSString *displayableTextCacheKey = [@"body-" stringByAppendingString:interactionId];
|
|
|
|
|
|
|
|
return [self displayableTextForCacheKey:displayableTextCacheKey
|
|
|
|
textBlock:^{
|
|
|
|
return text;
|
|
|
|
}];
|
2017-10-25 20:53:54 +02:00
|
|
|
}
|
|
|
|
|
2018-03-29 17:25:19 +02:00
|
|
|
- (DisplayableText *)displayableBodyTextForOversizeTextAttachment:(TSAttachmentStream *)attachmentStream
|
|
|
|
interactionId:(NSString *)interactionId
|
2017-10-25 20:53:54 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(attachmentStream);
|
|
|
|
OWSAssertDebug(interactionId.length > 0);
|
2017-10-25 20:53:54 +02:00
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
NSString *displayableTextCacheKey = [@"oversize-body-" stringByAppendingString:interactionId];
|
|
|
|
|
|
|
|
return [self displayableTextForCacheKey:displayableTextCacheKey
|
|
|
|
textBlock:^{
|
2018-09-04 16:25:42 +02:00
|
|
|
NSData *textData =
|
|
|
|
[NSData dataWithContentsOfURL:attachmentStream.originalMediaURL];
|
2018-04-03 16:32:15 +02:00
|
|
|
NSString *text =
|
|
|
|
[[NSString alloc] initWithData:textData encoding:NSUTF8StringEncoding];
|
|
|
|
return text;
|
|
|
|
}];
|
2017-10-25 20:53:54 +02:00
|
|
|
}
|
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
- (DisplayableText *)displayableQuotedTextForText:(NSString *)text interactionId:(NSString *)interactionId
|
2017-10-25 20:53:54 +02:00
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(text);
|
|
|
|
OWSAssertDebug(interactionId.length > 0);
|
2017-10-25 20:53:54 +02:00
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
NSString *displayableTextCacheKey = [@"quoted-" stringByAppendingString:interactionId];
|
|
|
|
|
|
|
|
return [self displayableTextForCacheKey:displayableTextCacheKey
|
|
|
|
textBlock:^{
|
|
|
|
return text;
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2018-11-07 16:36:53 +01:00
|
|
|
- (DisplayableText *)displayableCaptionForText:(NSString *)text attachmentId:(NSString *)attachmentId
|
|
|
|
{
|
|
|
|
OWSAssertDebug(text);
|
|
|
|
OWSAssertDebug(attachmentId.length > 0);
|
|
|
|
|
|
|
|
NSString *displayableTextCacheKey = [@"attachment-caption-" stringByAppendingString:attachmentId];
|
|
|
|
|
|
|
|
return [self displayableTextForCacheKey:displayableTextCacheKey
|
|
|
|
textBlock:^{
|
|
|
|
return text;
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
- (DisplayableText *)displayableTextForCacheKey:(NSString *)displayableTextCacheKey
|
|
|
|
textBlock:(NSString * (^_Nonnull)(void))textBlock
|
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(displayableTextCacheKey.length > 0);
|
2018-04-03 16:32:15 +02:00
|
|
|
|
|
|
|
DisplayableText *_Nullable displayableText = [[self displayableTextCache] objectForKey:displayableTextCacheKey];
|
|
|
|
if (!displayableText) {
|
2017-10-25 20:53:54 +02:00
|
|
|
NSString *text = textBlock();
|
2018-04-03 16:32:15 +02:00
|
|
|
displayableText = [DisplayableText displayableText:text];
|
|
|
|
[[self displayableTextCache] setObject:displayableText forKey:displayableTextCacheKey];
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
2018-04-03 16:32:15 +02:00
|
|
|
return displayableText;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
#pragma mark - View State
|
|
|
|
|
2017-11-16 17:22:16 +01:00
|
|
|
- (void)ensureViewState:(YapDatabaseReadTransaction *)transaction
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2017-12-19 17:38:25 +01:00
|
|
|
OWSAssertIsOnMainThread();
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(transaction);
|
|
|
|
OWSAssertDebug(!self.hasViewState);
|
2017-11-16 17:22:16 +01:00
|
|
|
|
2018-08-09 16:47:43 +02:00
|
|
|
switch (self.interaction.interactionType) {
|
|
|
|
case OWSInteractionType_Unknown:
|
|
|
|
case OWSInteractionType_Offer:
|
2018-11-01 16:39:04 +01:00
|
|
|
case OWSInteractionType_TypingIndicator:
|
2018-08-09 16:47:43 +02:00
|
|
|
return;
|
|
|
|
case OWSInteractionType_Info:
|
|
|
|
case OWSInteractionType_Call:
|
|
|
|
self.systemMessageText = [self systemMessageTextWithTransaction:transaction];
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(self.systemMessageText.length > 0);
|
2018-08-09 16:47:43 +02:00
|
|
|
return;
|
|
|
|
case OWSInteractionType_IncomingMessage:
|
|
|
|
case OWSInteractionType_OutgoingMessage:
|
|
|
|
break;
|
|
|
|
default:
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSFailDebug(@"Unknown interaction type.");
|
2018-08-09 16:47:43 +02:00
|
|
|
return;
|
2017-11-16 19:23:47 +01:00
|
|
|
}
|
2017-10-10 22:13:54 +02:00
|
|
|
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug([self.interaction isKindOfClass:[TSOutgoingMessage class]] ||
|
2018-08-09 16:47:43 +02:00
|
|
|
[self.interaction isKindOfClass:[TSIncomingMessage class]]);
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
self.hasViewState = YES;
|
|
|
|
|
2017-10-26 20:34:30 +02:00
|
|
|
TSMessage *message = (TSMessage *)self.interaction;
|
2021-08-05 05:34:07 +02:00
|
|
|
|
|
|
|
if (message.isDeleted) {
|
|
|
|
self.messageCellType = OWSMessageCellType_DeletedMessage;
|
|
|
|
return;
|
|
|
|
}
|
2018-11-05 17:07:00 +01:00
|
|
|
|
2018-11-30 16:59:11 +01:00
|
|
|
// Check for quoted replies _before_ media album handling,
|
|
|
|
// since that logic may exit early.
|
|
|
|
if (message.quotedMessage) {
|
|
|
|
self.quotedReply =
|
2019-10-09 03:40:39 +02:00
|
|
|
[OWSQuotedReplyModel quotedReplyWithQuotedMessage:message.quotedMessage threadId:message.uniqueThreadId transaction:transaction];
|
2018-11-30 16:59:11 +01:00
|
|
|
|
|
|
|
if (self.quotedReply.body.length > 0) {
|
|
|
|
self.displayableQuotedText =
|
|
|
|
[self displayableQuotedTextForText:self.quotedReply.body interactionId:message.uniqueId];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-20 18:32:41 +01:00
|
|
|
TSAttachment *_Nullable oversizeTextAttachment = [message oversizeTextAttachmentWithTransaction:transaction];
|
2019-03-15 18:21:54 +01:00
|
|
|
if ([oversizeTextAttachment isKindOfClass:[TSAttachmentStream class]]) {
|
2019-02-20 18:32:41 +01:00
|
|
|
TSAttachmentStream *oversizeTextAttachmentStream = (TSAttachmentStream *)oversizeTextAttachment;
|
|
|
|
self.displayableBodyText = [self displayableBodyTextForOversizeTextAttachment:oversizeTextAttachmentStream
|
|
|
|
interactionId:message.uniqueId];
|
2019-03-15 18:21:54 +01:00
|
|
|
} else if ([oversizeTextAttachment isKindOfClass:[TSAttachmentPointer class]]) {
|
|
|
|
TSAttachmentPointer *oversizeTextAttachmentPointer = (TSAttachmentPointer *)oversizeTextAttachment;
|
|
|
|
// TODO: Handle backup restore.
|
2019-03-15 18:22:54 +01:00
|
|
|
self.messageCellType = OWSMessageCellType_OversizeTextDownloading;
|
2019-03-15 18:21:54 +01:00
|
|
|
self.attachmentPointer = (TSAttachmentPointer *)oversizeTextAttachmentPointer;
|
|
|
|
return;
|
2019-02-23 01:37:03 +01:00
|
|
|
} else {
|
|
|
|
NSString *_Nullable bodyText = [message bodyTextWithTransaction:transaction];
|
|
|
|
if (bodyText) {
|
|
|
|
self.displayableBodyText = [self displayableBodyTextForText:bodyText interactionId:message.uniqueId];
|
|
|
|
}
|
2019-02-20 18:32:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
NSArray<TSAttachment *> *mediaAttachments = [message mediaAttachmentsWithTransaction:transaction];
|
2019-02-27 17:03:35 +01:00
|
|
|
NSArray<ConversationMediaAlbumItem *> *mediaAlbumItems = [self albumItemsForMediaAttachments:mediaAttachments];
|
2019-02-23 01:37:03 +01:00
|
|
|
if (mediaAlbumItems.count > 0) {
|
2018-11-08 19:20:14 +01:00
|
|
|
if (mediaAlbumItems.count == 1) {
|
|
|
|
ConversationMediaAlbumItem *mediaAlbumItem = mediaAlbumItems.firstObject;
|
|
|
|
if (mediaAlbumItem.attachmentStream && !mediaAlbumItem.attachmentStream.isValidVisualMedia) {
|
|
|
|
OWSLogWarn(@"Treating invalid media as generic attachment.");
|
|
|
|
self.messageCellType = OWSMessageCellType_GenericAttachment;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-07 17:52:34 +01:00
|
|
|
self.mediaAlbumItems = mediaAlbumItems;
|
2019-02-25 17:20:06 +01:00
|
|
|
self.messageCellType = OWSMessageCellType_MediaMessage;
|
2018-11-05 17:07:00 +01:00
|
|
|
return;
|
|
|
|
}
|
2019-02-20 18:32:41 +01:00
|
|
|
|
2018-11-05 17:07:00 +01:00
|
|
|
// Only media galleries should have more than one attachment.
|
2019-02-20 18:32:41 +01:00
|
|
|
OWSAssertDebug(mediaAttachments.count <= 1);
|
2019-02-23 01:37:03 +01:00
|
|
|
|
2019-02-20 18:32:41 +01:00
|
|
|
TSAttachment *_Nullable mediaAttachment = mediaAttachments.firstObject;
|
|
|
|
if (mediaAttachment) {
|
|
|
|
if ([mediaAttachment isKindOfClass:[TSAttachmentStream class]]) {
|
|
|
|
self.attachmentStream = (TSAttachmentStream *)mediaAttachment;
|
|
|
|
if ([self.attachmentStream isAudio]) {
|
2017-11-17 17:56:48 +01:00
|
|
|
CGFloat audioDurationSeconds = [self.attachmentStream audioDurationSeconds];
|
|
|
|
if (audioDurationSeconds > 0) {
|
2017-11-20 20:50:43 +01:00
|
|
|
self.audioDurationSeconds = audioDurationSeconds;
|
2017-11-17 17:56:48 +01:00
|
|
|
self.messageCellType = OWSMessageCellType_Audio;
|
|
|
|
} else {
|
|
|
|
self.messageCellType = OWSMessageCellType_GenericAttachment;
|
|
|
|
}
|
2019-02-20 18:32:41 +01:00
|
|
|
} else if (self.messageCellType == OWSMessageCellType_Unknown) {
|
2017-10-26 20:34:30 +02:00
|
|
|
self.messageCellType = OWSMessageCellType_GenericAttachment;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
2019-02-20 18:32:41 +01:00
|
|
|
} else if ([mediaAttachment isKindOfClass:[TSAttachmentPointer class]]) {
|
2019-03-15 16:39:21 +01:00
|
|
|
if ([mediaAttachment isAudio]) {
|
|
|
|
self.audioDurationSeconds = 0;
|
|
|
|
self.messageCellType = OWSMessageCellType_Audio;
|
|
|
|
} else {
|
2019-03-15 17:44:02 +01:00
|
|
|
self.messageCellType = OWSMessageCellType_GenericAttachment;
|
2019-03-15 16:39:21 +01:00
|
|
|
}
|
2019-02-20 18:32:41 +01:00
|
|
|
self.attachmentPointer = (TSAttachmentPointer *)mediaAttachment;
|
2017-10-19 19:04:49 +02:00
|
|
|
} else {
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSFailDebug(@"Unknown attachment type");
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
2017-12-18 18:11:02 +01:00
|
|
|
}
|
|
|
|
|
2019-02-20 18:32:41 +01:00
|
|
|
if (self.hasBodyText) {
|
2017-12-18 18:11:02 +01:00
|
|
|
if (self.messageCellType == OWSMessageCellType_Unknown) {
|
2019-02-25 17:20:06 +01:00
|
|
|
self.messageCellType = OWSMessageCellType_TextOnlyMessage;
|
2017-12-18 18:11:02 +01:00
|
|
|
}
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(self.displayableBodyText);
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
|
2019-02-20 18:32:41 +01:00
|
|
|
if (self.hasBodyText && message.linkPreview) {
|
2019-01-18 15:54:12 +01:00
|
|
|
self.linkPreview = message.linkPreview;
|
2020-08-19 08:41:25 +02:00
|
|
|
if (message.linkPreview.imageAttachmentId && message.linkPreview.imageAttachmentId.length > 0) {
|
2019-01-25 21:33:32 +01:00
|
|
|
TSAttachment *_Nullable linkPreviewAttachment =
|
2019-01-18 15:54:12 +01:00
|
|
|
[TSAttachment fetchObjectWithUniqueID:message.linkPreview.imageAttachmentId transaction:transaction];
|
2019-01-25 21:33:32 +01:00
|
|
|
if (!linkPreviewAttachment) {
|
2022-01-28 03:14:02 +01:00
|
|
|
OWSLogDebug(@"Could not load link preview image attachment.");
|
2019-01-25 21:33:32 +01:00
|
|
|
} else if (!linkPreviewAttachment.isImage) {
|
2022-01-28 03:14:02 +01:00
|
|
|
OWSLogDebug(@"Link preview attachment isn't an image.");
|
2019-01-25 21:33:32 +01:00
|
|
|
} else if ([linkPreviewAttachment isKindOfClass:[TSAttachmentStream class]]) {
|
|
|
|
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)linkPreviewAttachment;
|
|
|
|
if (!attachmentStream.isValidImage) {
|
2022-01-28 03:14:02 +01:00
|
|
|
OWSLogDebug(@"Link preview image attachment isn't valid.");
|
2019-01-25 21:33:32 +01:00
|
|
|
} else {
|
|
|
|
self.linkPreviewAttachment = linkPreviewAttachment;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
self.linkPreviewAttachment = linkPreviewAttachment;
|
2019-01-18 15:54:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 18:11:02 +01:00
|
|
|
if (self.messageCellType == OWSMessageCellType_Unknown) {
|
|
|
|
// Messages of unknown type (including messages with missing attachments)
|
|
|
|
// are rendered like empty text messages, but without any interactivity.
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSLogWarn(@"Treating unknown message as empty text message: %@ %llu", message.class, message.timestamp);
|
2019-02-25 17:20:06 +01:00
|
|
|
self.messageCellType = OWSMessageCellType_TextOnlyMessage;
|
2018-03-29 17:25:19 +02:00
|
|
|
self.displayableBodyText = [[DisplayableText alloc] initWithFullText:@"" displayText:@"" isTextTruncated:NO];
|
2017-12-18 18:11:02 +01:00
|
|
|
}
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
2018-08-09 16:47:43 +02:00
|
|
|
|
2019-02-27 17:03:35 +01:00
|
|
|
- (NSArray<ConversationMediaAlbumItem *> *)albumItemsForMediaAttachments:(NSArray<TSAttachment *> *)attachments
|
2018-11-05 17:07:00 +01:00
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
|
|
|
|
2018-11-07 17:52:34 +01:00
|
|
|
NSMutableArray<ConversationMediaAlbumItem *> *mediaAlbumItems = [NSMutableArray new];
|
2018-11-05 17:07:00 +01:00
|
|
|
for (TSAttachment *attachment in attachments) {
|
2019-02-27 17:03:35 +01:00
|
|
|
if (!attachment.isVisualMedia) {
|
|
|
|
// Well behaving clients should not send a mix of visual media (like JPG) and non-visual media (like PDF's)
|
|
|
|
// Since we're not coped to handle a mix of media, return @[]
|
|
|
|
OWSAssertDebug(mediaAlbumItems.count == 0);
|
|
|
|
return @[];
|
|
|
|
}
|
|
|
|
|
2018-11-07 16:36:53 +01:00
|
|
|
NSString *_Nullable caption = (attachment.caption
|
|
|
|
? [self displayableCaptionForText:attachment.caption attachmentId:attachment.uniqueId].displayText
|
|
|
|
: nil);
|
|
|
|
|
2018-11-05 17:07:00 +01:00
|
|
|
if (![attachment isKindOfClass:[TSAttachmentStream class]]) {
|
2019-03-15 14:47:04 +01:00
|
|
|
TSAttachmentPointer *attachmentPointer = (TSAttachmentPointer *)attachment;
|
|
|
|
CGSize mediaSize = CGSizeZero;
|
|
|
|
if (attachmentPointer.mediaSize.width > 0 && attachmentPointer.mediaSize.height > 0) {
|
|
|
|
mediaSize = attachmentPointer.mediaSize;
|
|
|
|
}
|
2018-11-07 17:52:34 +01:00
|
|
|
[mediaAlbumItems addObject:[[ConversationMediaAlbumItem alloc] initWithAttachment:attachment
|
|
|
|
attachmentStream:nil
|
|
|
|
caption:caption
|
2019-03-15 14:47:04 +01:00
|
|
|
mediaSize:mediaSize]];
|
2018-11-05 17:07:00 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
TSAttachmentStream *attachmentStream = (TSAttachmentStream *)attachment;
|
|
|
|
if (![attachmentStream isValidVisualMedia]) {
|
|
|
|
OWSLogWarn(@"Filtering invalid media.");
|
2018-11-07 17:52:34 +01:00
|
|
|
[mediaAlbumItems addObject:[[ConversationMediaAlbumItem alloc] initWithAttachment:attachment
|
|
|
|
attachmentStream:nil
|
|
|
|
caption:caption
|
|
|
|
mediaSize:CGSizeZero]];
|
2018-11-05 17:07:00 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-11-05 22:43:46 +01:00
|
|
|
CGSize mediaSize = [attachmentStream imageSize];
|
|
|
|
if (mediaSize.width <= 0 || mediaSize.height <= 0) {
|
2018-11-05 17:07:00 +01:00
|
|
|
OWSLogWarn(@"Filtering media with invalid size.");
|
2018-11-07 17:52:34 +01:00
|
|
|
[mediaAlbumItems addObject:[[ConversationMediaAlbumItem alloc] initWithAttachment:attachment
|
|
|
|
attachmentStream:nil
|
|
|
|
caption:caption
|
|
|
|
mediaSize:CGSizeZero]];
|
2018-11-05 17:07:00 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-11-07 17:52:34 +01:00
|
|
|
ConversationMediaAlbumItem *mediaAlbumItem =
|
|
|
|
[[ConversationMediaAlbumItem alloc] initWithAttachment:attachment
|
|
|
|
attachmentStream:attachmentStream
|
|
|
|
caption:caption
|
|
|
|
mediaSize:mediaSize];
|
|
|
|
[mediaAlbumItems addObject:mediaAlbumItem];
|
2018-11-05 17:07:00 +01:00
|
|
|
}
|
2018-11-07 17:52:34 +01:00
|
|
|
return mediaAlbumItems;
|
2018-11-05 17:07:00 +01:00
|
|
|
}
|
|
|
|
|
2018-08-09 16:47:43 +02:00
|
|
|
- (NSString *)systemMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(transaction);
|
2018-08-09 16:47:43 +02:00
|
|
|
|
|
|
|
switch (self.interaction.interactionType) {
|
|
|
|
case OWSInteractionType_Info: {
|
|
|
|
TSInfoMessage *infoMessage = (TSInfoMessage *)self.interaction;
|
2020-11-16 00:34:47 +01:00
|
|
|
return [infoMessage previewTextWithTransaction:transaction];
|
2018-08-09 16:47:43 +02:00
|
|
|
}
|
|
|
|
default:
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSFailDebug(@"not a system message.");
|
2018-08-09 16:47:43 +02:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
2017-10-10 22:13:54 +02:00
|
|
|
|
2018-04-06 22:53:16 +02:00
|
|
|
- (nullable NSString *)quotedAttachmentMimetype
|
|
|
|
{
|
|
|
|
return self.quotedReply.contentType;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (nullable NSString *)quotedRecipientId
|
|
|
|
{
|
|
|
|
return self.quotedReply.authorId;
|
|
|
|
}
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
- (OWSMessageCellType)messageCellType
|
|
|
|
{
|
2017-12-19 17:38:25 +01:00
|
|
|
OWSAssertIsOnMainThread();
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
return _messageCellType;
|
|
|
|
}
|
|
|
|
|
2018-03-29 17:25:19 +02:00
|
|
|
- (nullable DisplayableText *)displayableBodyText
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2017-12-19 17:38:25 +01:00
|
|
|
OWSAssertIsOnMainThread();
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(self.hasViewState);
|
2017-10-10 22:13:54 +02:00
|
|
|
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(_displayableBodyText);
|
|
|
|
OWSAssertDebug(_displayableBodyText.displayText);
|
|
|
|
OWSAssertDebug(_displayableBodyText.fullText);
|
2017-10-25 20:53:54 +02:00
|
|
|
|
2018-03-29 17:25:19 +02:00
|
|
|
return _displayableBodyText;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (nullable TSAttachmentStream *)attachmentStream
|
|
|
|
{
|
2017-12-19 17:38:25 +01:00
|
|
|
OWSAssertIsOnMainThread();
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(self.hasViewState);
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
return _attachmentStream;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (nullable TSAttachmentPointer *)attachmentPointer
|
|
|
|
{
|
2017-12-19 17:38:25 +01:00
|
|
|
OWSAssertIsOnMainThread();
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(self.hasViewState);
|
2017-10-10 22:13:54 +02:00
|
|
|
|
|
|
|
return _attachmentPointer;
|
|
|
|
}
|
|
|
|
|
2018-04-03 16:32:15 +02:00
|
|
|
- (nullable DisplayableText *)displayableQuotedText
|
|
|
|
{
|
|
|
|
OWSAssertIsOnMainThread();
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(self.hasViewState);
|
2018-04-03 16:32:15 +02:00
|
|
|
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(_displayableQuotedText);
|
|
|
|
OWSAssertDebug(_displayableQuotedText.displayText);
|
|
|
|
OWSAssertDebug(_displayableQuotedText.fullText);
|
2018-04-03 16:32:15 +02:00
|
|
|
|
|
|
|
return _displayableQuotedText;
|
|
|
|
}
|
|
|
|
|
2017-12-19 19:35:39 +01:00
|
|
|
- (void)copyTextAction
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2019-03-15 18:21:54 +01:00
|
|
|
if (self.attachmentPointer != nil) {
|
|
|
|
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
switch (self.messageCellType) {
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_TextOnlyMessage:
|
2017-12-19 19:35:39 +01:00
|
|
|
case OWSMessageCellType_Audio:
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_MediaMessage:
|
2017-12-19 19:35:39 +01:00
|
|
|
case OWSMessageCellType_GenericAttachment: {
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(self.displayableBodyText);
|
2018-03-29 17:25:19 +02:00
|
|
|
[UIPasteboard.generalPasteboard setString:self.displayableBodyText.fullText];
|
2017-10-10 22:13:54 +02:00
|
|
|
break;
|
2017-12-19 19:35:39 +01:00
|
|
|
}
|
|
|
|
case OWSMessageCellType_Unknown: {
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSFailDebug(@"No text to copy");
|
2017-12-19 19:35:39 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-03-15 18:21:54 +01:00
|
|
|
case OWSMessageCellType_OversizeTextDownloading:
|
|
|
|
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
|
|
|
|
return;
|
2017-12-19 19:35:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)copyMediaAction
|
|
|
|
{
|
2019-03-15 18:21:54 +01:00
|
|
|
if (self.attachmentPointer != nil) {
|
|
|
|
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-19 19:35:39 +01:00
|
|
|
switch (self.messageCellType) {
|
|
|
|
case OWSMessageCellType_Unknown:
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_TextOnlyMessage:
|
2017-10-10 22:13:54 +02:00
|
|
|
case OWSMessageCellType_Audio:
|
|
|
|
case OWSMessageCellType_GenericAttachment: {
|
2018-11-06 16:02:13 +01:00
|
|
|
[self copyAttachmentToPasteboard:self.attachmentStream];
|
2017-10-10 22:13:54 +02:00
|
|
|
break;
|
|
|
|
}
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_MediaMessage: {
|
2018-11-08 19:20:14 +01:00
|
|
|
if (self.mediaAlbumItems.count == 1) {
|
|
|
|
ConversationMediaAlbumItem *mediaAlbumItem = self.mediaAlbumItems.firstObject;
|
|
|
|
if (mediaAlbumItem.attachmentStream && mediaAlbumItem.attachmentStream.isValidVisualMedia) {
|
|
|
|
[self copyAttachmentToPasteboard:mediaAlbumItem.attachmentStream];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-07 18:39:40 +01:00
|
|
|
OWSFailDebug(@"Can't copy media album");
|
2018-11-05 17:07:00 +01:00
|
|
|
break;
|
|
|
|
}
|
2019-03-15 18:21:54 +01:00
|
|
|
case OWSMessageCellType_OversizeTextDownloading:
|
|
|
|
OWSFailDebug(@"Can't copy not-yet-downloaded attachment");
|
|
|
|
return;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-06 16:02:13 +01:00
|
|
|
- (void)copyAttachmentToPasteboard:(TSAttachmentStream *)attachment
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2018-11-06 16:02:13 +01:00
|
|
|
OWSAssertDebug(attachment);
|
|
|
|
|
|
|
|
NSString *utiType = [MIMETypeUtil utiTypeForMIMEType:attachment.contentType];
|
|
|
|
if (!utiType) {
|
|
|
|
OWSFailDebug(@"Unknown MIME type: %@", attachment.contentType);
|
|
|
|
utiType = (NSString *)kUTTypeGIF;
|
2017-12-19 19:35:39 +01:00
|
|
|
}
|
2018-11-06 16:02:13 +01:00
|
|
|
NSData *data = [NSData dataWithContentsOfURL:[attachment originalMediaURL]];
|
|
|
|
if (!data) {
|
|
|
|
OWSFailDebug(@"Could not load attachment data");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
[UIPasteboard.generalPasteboard setData:data forPasteboardType:utiType];
|
2017-12-19 19:35:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)shareMediaAction
|
|
|
|
{
|
2019-03-15 18:21:54 +01:00
|
|
|
if (self.attachmentPointer != nil) {
|
|
|
|
OWSFailDebug(@"Can't share not-yet-downloaded attachment");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-12-19 19:35:39 +01:00
|
|
|
switch (self.messageCellType) {
|
|
|
|
case OWSMessageCellType_Unknown:
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_TextOnlyMessage:
|
2017-10-10 22:13:54 +02:00
|
|
|
case OWSMessageCellType_Audio:
|
|
|
|
case OWSMessageCellType_GenericAttachment:
|
|
|
|
[AttachmentSharing showShareUIForAttachment:self.attachmentStream];
|
|
|
|
break;
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_MediaMessage: {
|
2018-11-05 17:07:00 +01:00
|
|
|
// TODO: We need a "canShareMediaAction" method.
|
2018-11-07 17:52:34 +01:00
|
|
|
OWSAssertDebug(self.mediaAlbumItems);
|
2018-11-05 17:07:00 +01:00
|
|
|
NSMutableArray<TSAttachmentStream *> *attachmentStreams = [NSMutableArray new];
|
2019-01-22 21:31:42 +01:00
|
|
|
for (ConversationMediaAlbumItem *mediaAlbumItem in self.mediaAlbumItems) {
|
|
|
|
if (mediaAlbumItem.attachmentStream && mediaAlbumItem.attachmentStream.isValidVisualMedia) {
|
|
|
|
[attachmentStreams addObject:mediaAlbumItem.attachmentStream];
|
2018-11-05 17:07:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (attachmentStreams.count < 1) {
|
2018-11-07 18:39:40 +01:00
|
|
|
OWSFailDebug(@"Can't share media album; no valid items.");
|
2018-11-05 17:07:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
[AttachmentSharing showShareUIForAttachments:attachmentStreams completion:nil];
|
|
|
|
break;
|
|
|
|
}
|
2019-03-15 18:21:54 +01:00
|
|
|
case OWSMessageCellType_OversizeTextDownloading:
|
|
|
|
OWSFailDebug(@"Can't share not-yet-downloaded attachment");
|
|
|
|
return;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-06 16:08:14 +01:00
|
|
|
- (BOOL)canCopyMedia
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2019-03-15 18:21:54 +01:00
|
|
|
if (self.attachmentPointer != nil) {
|
2019-03-18 19:24:33 +01:00
|
|
|
// The attachment is still downloading.
|
2019-03-15 18:21:54 +01:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
switch (self.messageCellType) {
|
2017-12-19 19:35:39 +01:00
|
|
|
case OWSMessageCellType_Unknown:
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_TextOnlyMessage:
|
2017-10-10 22:13:54 +02:00
|
|
|
case OWSMessageCellType_Audio:
|
|
|
|
return NO;
|
|
|
|
case OWSMessageCellType_GenericAttachment:
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_MediaMessage: {
|
2018-11-08 19:20:14 +01:00
|
|
|
if (self.mediaAlbumItems.count == 1) {
|
|
|
|
ConversationMediaAlbumItem *mediaAlbumItem = self.mediaAlbumItems.firstObject;
|
|
|
|
if (mediaAlbumItem.attachmentStream && mediaAlbumItem.attachmentStream.isValidVisualMedia) {
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
2017-10-10 22:13:54 +02:00
|
|
|
return NO;
|
2018-11-08 19:20:14 +01:00
|
|
|
}
|
2019-03-15 18:21:54 +01:00
|
|
|
case OWSMessageCellType_OversizeTextDownloading:
|
|
|
|
return NO;
|
2018-11-06 16:08:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)canSaveMedia
|
|
|
|
{
|
2019-03-15 18:21:54 +01:00
|
|
|
if (self.attachmentPointer != nil) {
|
2019-03-18 19:24:33 +01:00
|
|
|
// The attachment is still downloading.
|
2019-03-15 18:21:54 +01:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2018-11-06 16:08:14 +01:00
|
|
|
switch (self.messageCellType) {
|
|
|
|
case OWSMessageCellType_Unknown:
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_TextOnlyMessage:
|
2018-11-06 16:08:14 +01:00
|
|
|
case OWSMessageCellType_Audio:
|
|
|
|
return NO;
|
|
|
|
case OWSMessageCellType_GenericAttachment:
|
2017-10-10 22:13:54 +02:00
|
|
|
return NO;
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_MediaMessage: {
|
2018-11-07 17:52:34 +01:00
|
|
|
for (ConversationMediaAlbumItem *mediaAlbumItem in self.mediaAlbumItems) {
|
|
|
|
if (!mediaAlbumItem.attachmentStream) {
|
2018-11-05 17:07:00 +01:00
|
|
|
continue;
|
|
|
|
}
|
2018-11-08 19:20:14 +01:00
|
|
|
if (!mediaAlbumItem.attachmentStream.isValidVisualMedia) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-11-07 17:52:34 +01:00
|
|
|
if (mediaAlbumItem.attachmentStream.isImage || mediaAlbumItem.attachmentStream.isAnimated) {
|
2018-11-05 17:07:00 +01:00
|
|
|
return YES;
|
|
|
|
}
|
2018-11-07 17:52:34 +01:00
|
|
|
if (mediaAlbumItem.attachmentStream.isVideo) {
|
2018-11-06 16:02:13 +01:00
|
|
|
if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(
|
2018-11-07 17:52:34 +01:00
|
|
|
mediaAlbumItem.attachmentStream.originalFilePath)) {
|
2018-11-05 17:07:00 +01:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
2019-03-15 18:21:54 +01:00
|
|
|
case OWSMessageCellType_OversizeTextDownloading:
|
|
|
|
return NO;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-19 19:35:39 +01:00
|
|
|
- (void)saveMediaAction
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2019-03-15 18:21:54 +01:00
|
|
|
if (self.attachmentPointer != nil) {
|
|
|
|
OWSFailDebug(@"Can't save not-yet-downloaded attachment");
|
|
|
|
return;
|
|
|
|
}
|
2017-10-10 22:13:54 +02:00
|
|
|
switch (self.messageCellType) {
|
2017-12-19 19:35:39 +01:00
|
|
|
case OWSMessageCellType_Unknown:
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_TextOnlyMessage:
|
2017-10-10 22:13:54 +02:00
|
|
|
case OWSMessageCellType_Audio:
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSFailDebug(@"Cannot save media data.");
|
2017-10-10 22:13:54 +02:00
|
|
|
break;
|
|
|
|
case OWSMessageCellType_GenericAttachment:
|
2018-08-27 18:51:32 +02:00
|
|
|
OWSFailDebug(@"Cannot save media data.");
|
2017-10-10 22:13:54 +02:00
|
|
|
break;
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_MediaMessage: {
|
2019-01-22 22:50:49 +01:00
|
|
|
[self saveMediaAlbumItems];
|
|
|
|
break;
|
|
|
|
}
|
2019-03-15 18:21:54 +01:00
|
|
|
case OWSMessageCellType_OversizeTextDownloading:
|
|
|
|
OWSFailDebug(@"Can't save not-yet-downloaded attachment");
|
|
|
|
return;
|
2019-01-22 22:50:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)saveMediaAlbumItems
|
|
|
|
{
|
|
|
|
// We need to do these writes serially to avoid "write busy" errors
|
|
|
|
// from too many concurrent asset saves.
|
|
|
|
[self saveMediaAlbumItems:[self.mediaAlbumItems mutableCopy]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)saveMediaAlbumItems:(NSMutableArray<ConversationMediaAlbumItem *> *)mediaAlbumItems
|
|
|
|
{
|
|
|
|
if (mediaAlbumItems.count < 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ConversationMediaAlbumItem *mediaAlbumItem = mediaAlbumItems.firstObject;
|
|
|
|
[mediaAlbumItems removeObjectAtIndex:0];
|
|
|
|
|
|
|
|
if (!mediaAlbumItem.attachmentStream || !mediaAlbumItem.attachmentStream.isValidVisualMedia) {
|
|
|
|
// Skip this item.
|
|
|
|
} else if (mediaAlbumItem.attachmentStream.isImage || mediaAlbumItem.attachmentStream.isAnimated) {
|
|
|
|
[[PHPhotoLibrary sharedPhotoLibrary]
|
|
|
|
performChanges:^{
|
|
|
|
[PHAssetChangeRequest
|
|
|
|
creationRequestForAssetFromImageAtFileURL:mediaAlbumItem.attachmentStream.originalMediaURL];
|
|
|
|
}
|
|
|
|
completionHandler:^(BOOL success, NSError *error) {
|
|
|
|
if (error || !success) {
|
|
|
|
OWSFailDebug(@"Image save failed: %@", error);
|
2018-11-05 17:07:00 +01:00
|
|
|
}
|
2019-01-22 22:50:49 +01:00
|
|
|
[self saveMediaAlbumItems:mediaAlbumItems];
|
|
|
|
}];
|
|
|
|
return;
|
|
|
|
} else if (mediaAlbumItem.attachmentStream.isVideo) {
|
|
|
|
[[PHPhotoLibrary sharedPhotoLibrary]
|
|
|
|
performChanges:^{
|
|
|
|
[PHAssetChangeRequest
|
|
|
|
creationRequestForAssetFromVideoAtFileURL:mediaAlbumItem.attachmentStream.originalMediaURL];
|
2018-11-05 17:07:00 +01:00
|
|
|
}
|
2019-01-22 22:50:49 +01:00
|
|
|
completionHandler:^(BOOL success, NSError *error) {
|
|
|
|
if (error || !success) {
|
|
|
|
OWSFailDebug(@"Video save failed: %@", error);
|
|
|
|
}
|
|
|
|
[self saveMediaAlbumItems:mediaAlbumItems];
|
|
|
|
}];
|
|
|
|
return;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
2019-01-22 22:50:49 +01:00
|
|
|
return [self saveMediaAlbumItems:mediaAlbumItems];
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
|
2021-07-30 08:51:43 +02:00
|
|
|
- (void)deleteLocallyAction
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2021-08-06 09:30:18 +02:00
|
|
|
TSMessage *message = (TSMessage *)self.interaction;
|
2020-11-27 00:08:46 +01:00
|
|
|
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
2021-08-06 09:30:18 +02:00
|
|
|
[MessageInvalidator invalidate:message with:transaction];
|
2020-11-27 00:08:46 +01:00
|
|
|
[self.interaction removeWithTransaction:transaction];
|
|
|
|
if (self.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
|
|
|
|
[LKStorage.shared cancelPendingMessageSendJobIfNeededForMessage:self.interaction.timestamp using:transaction];
|
|
|
|
}
|
|
|
|
}];
|
2021-07-30 08:51:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)deleteRemotelyAction
|
|
|
|
{
|
2021-08-02 09:07:29 +02:00
|
|
|
TSMessage *message = (TSMessage *)self.interaction;
|
2019-08-28 08:07:14 +02:00
|
|
|
|
2019-08-28 07:18:42 +02:00
|
|
|
if (self.isGroupThread) {
|
|
|
|
TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread;
|
|
|
|
|
|
|
|
// Only allow deletion on incoming and outgoing messages
|
|
|
|
OWSInteractionType interationType = self.interaction.interactionType;
|
2019-09-02 05:27:12 +02:00
|
|
|
if (interationType != OWSInteractionType_IncomingMessage && interationType != OWSInteractionType_OutgoingMessage) return;
|
2019-08-28 07:18:42 +02:00
|
|
|
|
2021-07-30 08:59:01 +02:00
|
|
|
if (groupThread.isOpenGroup) {
|
|
|
|
// Make sure it's an open group message
|
|
|
|
if (!message.isOpenGroupMessage) return;
|
|
|
|
|
|
|
|
// Get the open group
|
|
|
|
SNOpenGroupV2 *openGroupV2 = [LKStorage.shared getV2OpenGroupForThreadID:groupThread.uniqueId];
|
|
|
|
if (openGroupV2 == nil) return;
|
|
|
|
|
|
|
|
// If it's an incoming message the user must have moderator status
|
|
|
|
if (self.interaction.interactionType == OWSInteractionType_IncomingMessage) {
|
|
|
|
NSString *userPublicKey = [LKStorage.shared getUserPublicKey];
|
|
|
|
if (![SNOpenGroupAPIV2 isUserModerator:userPublicKey forRoom:openGroupV2.room onServer:openGroupV2.server]) { return; }
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the message
|
|
|
|
[[SNOpenGroupAPIV2 deleteMessageWithServerID:message.openGroupServerMessageID fromRoom:openGroupV2.room onServer:openGroupV2.server].catch(^(NSError *error) {
|
|
|
|
// Roll back
|
|
|
|
[self.interaction save];
|
|
|
|
}) retainUntilComplete];
|
2021-08-02 09:07:29 +02:00
|
|
|
} else {
|
|
|
|
NSString *groupPublicKey = [LKGroupUtilities getDecodedGroupID:groupThread.groupModel.groupId];
|
2021-08-04 06:48:32 +02:00
|
|
|
[[SNSnodeAPI deleteMessageForPublickKey:groupPublicKey serverHashes:@[message.serverHash]].catch(^(NSError *error) {
|
2021-08-03 02:26:18 +02:00
|
|
|
// Roll back
|
|
|
|
[self.interaction save];
|
|
|
|
}) retainUntilComplete];
|
2021-07-30 08:59:01 +02:00
|
|
|
}
|
2021-08-02 09:07:29 +02:00
|
|
|
} else {
|
|
|
|
TSContactThread *contactThread = (TSContactThread *)self.interaction.thread;
|
2021-08-04 06:48:32 +02:00
|
|
|
[[SNSnodeAPI deleteMessageForPublickKey:contactThread.contactSessionID serverHashes:@[message.serverHash]].catch(^(NSError *error) {
|
2021-08-03 02:26:18 +02:00
|
|
|
// Roll back
|
|
|
|
[self.interaction save];
|
|
|
|
}) retainUntilComplete];
|
2019-08-28 07:18:42 +02:00
|
|
|
}
|
2021-08-02 09:07:29 +02:00
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
|
2021-08-09 05:51:09 +02:00
|
|
|
// Remove this after the unsend request is enabled
|
|
|
|
- (void)deleteAction
|
|
|
|
{
|
|
|
|
[LKStorage writeWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
|
|
|
[self.interaction removeWithTransaction:transaction];
|
|
|
|
if (self.interaction.interactionType == OWSInteractionType_OutgoingMessage) {
|
|
|
|
[LKStorage.shared cancelPendingMessageSendJobIfNeededForMessage:self.interaction.timestamp using:transaction];
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
|
|
|
|
if (self.isGroupThread) {
|
|
|
|
TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread;
|
|
|
|
|
|
|
|
// Only allow deletion on incoming and outgoing messages
|
|
|
|
OWSInteractionType interationType = self.interaction.interactionType;
|
|
|
|
if (interationType != OWSInteractionType_IncomingMessage && interationType != OWSInteractionType_OutgoingMessage) return;
|
|
|
|
|
|
|
|
// Make sure it's an open group message
|
|
|
|
TSMessage *message = (TSMessage *)self.interaction;
|
|
|
|
if (!message.isOpenGroupMessage) return;
|
|
|
|
|
|
|
|
// Get the open group
|
|
|
|
SNOpenGroupV2 *openGroupV2 = [LKStorage.shared getV2OpenGroupForThreadID:groupThread.uniqueId];
|
|
|
|
if (openGroup == nil && openGroupV2 == nil) return;
|
|
|
|
|
|
|
|
// If it's an incoming message the user must have moderator status
|
|
|
|
if (self.interaction.interactionType == OWSInteractionType_IncomingMessage) {
|
|
|
|
NSString *userPublicKey = [LKStorage.shared getUserPublicKey];
|
|
|
|
if (openGroupV2 != nil) {
|
|
|
|
if (![SNOpenGroupAPIV2 isUserModerator:userPublicKey forRoom:openGroupV2.room onServer:openGroupV2.server]) { return; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the message
|
|
|
|
BOOL wasSentByUser = (interationType == OWSInteractionType_OutgoingMessage);
|
|
|
|
if (openGroupV2 != nil) {
|
|
|
|
[[SNOpenGroupAPIV2 deleteMessageWithServerID:message.openGroupServerMessageID fromRoom:openGroupV2.room onServer:openGroupV2.server].catch(^(NSError *error) {
|
|
|
|
// Roll back
|
|
|
|
[self.interaction save];
|
|
|
|
}) retainUntilComplete];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-29 17:25:19 +02:00
|
|
|
- (BOOL)hasBodyTextActionContent
|
2017-12-19 19:35:39 +01:00
|
|
|
{
|
2018-03-29 17:25:19 +02:00
|
|
|
return self.hasBodyText && self.displayableBodyText.fullText.length > 0;
|
2017-12-19 19:35:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)hasMediaActionContent
|
2017-10-10 22:13:54 +02:00
|
|
|
{
|
2019-03-15 18:21:54 +01:00
|
|
|
if (self.attachmentPointer != nil) {
|
2019-03-18 19:24:33 +01:00
|
|
|
// The attachment is still downloading.
|
2019-03-15 18:21:54 +01:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
switch (self.messageCellType) {
|
2017-12-19 19:35:39 +01:00
|
|
|
case OWSMessageCellType_Unknown:
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_TextOnlyMessage:
|
2017-10-10 22:13:54 +02:00
|
|
|
case OWSMessageCellType_Audio:
|
|
|
|
case OWSMessageCellType_GenericAttachment:
|
|
|
|
return self.attachmentStream != nil;
|
2019-02-25 17:20:06 +01:00
|
|
|
case OWSMessageCellType_MediaMessage:
|
2018-11-07 18:39:40 +01:00
|
|
|
return self.firstValidAlbumAttachment != nil;
|
2019-03-15 18:21:54 +01:00
|
|
|
case OWSMessageCellType_OversizeTextDownloading:
|
|
|
|
return NO;
|
2017-10-10 22:13:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 22:39:01 +01:00
|
|
|
- (BOOL)mediaAlbumHasFailedAttachment
|
|
|
|
{
|
2019-02-25 17:20:06 +01:00
|
|
|
OWSAssertDebug(self.messageCellType == OWSMessageCellType_MediaMessage);
|
2019-01-15 22:39:01 +01:00
|
|
|
OWSAssertDebug(self.mediaAlbumItems.count > 0);
|
|
|
|
|
|
|
|
for (ConversationMediaAlbumItem *mediaAlbumItem in self.mediaAlbumItems) {
|
|
|
|
if (mediaAlbumItem.isFailedDownload) {
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2019-09-02 05:27:12 +02:00
|
|
|
- (BOOL)userCanDeleteGroupMessage
|
2019-08-28 07:18:42 +02:00
|
|
|
{
|
|
|
|
if (!self.isGroupThread) return false;
|
|
|
|
TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread;
|
|
|
|
|
|
|
|
// Only allow deletion on incoming and outgoing messages
|
|
|
|
OWSInteractionType interationType = self.interaction.interactionType;
|
|
|
|
if (interationType != OWSInteractionType_OutgoingMessage && interationType != OWSInteractionType_IncomingMessage) return false;
|
|
|
|
|
2021-01-21 01:27:52 +01:00
|
|
|
// Make sure it's an open group message
|
2019-08-28 07:18:42 +02:00
|
|
|
TSMessage *message = (TSMessage *)self.interaction;
|
2020-06-23 03:45:22 +02:00
|
|
|
if (!message.isOpenGroupMessage) return true;
|
2019-08-28 07:18:42 +02:00
|
|
|
|
2019-10-14 05:40:18 +02:00
|
|
|
// Ensure we have the details needed to contact the server
|
2021-03-24 04:36:26 +01:00
|
|
|
SNOpenGroupV2 *openGroupV2 = [LKStorage.shared getV2OpenGroupForThreadID:groupThread.uniqueId];
|
2021-05-05 06:22:29 +02:00
|
|
|
if (openGroupV2 == nil) return true;
|
2019-10-09 02:18:11 +02:00
|
|
|
|
2019-08-29 04:49:06 +02:00
|
|
|
if (interationType == OWSInteractionType_IncomingMessage) {
|
2019-11-14 06:47:02 +01:00
|
|
|
// Only allow deletion on incoming messages if the user has moderation permission
|
2021-03-24 04:36:26 +01:00
|
|
|
if (openGroupV2 != nil) {
|
|
|
|
return [SNOpenGroupAPIV2 isUserModerator:[SNGeneralUtilities getUserPublicKey] forRoom:openGroupV2.room onServer:openGroupV2.server];
|
|
|
|
}
|
2020-08-28 05:48:27 +02:00
|
|
|
} else {
|
2020-11-18 04:27:30 +01:00
|
|
|
return YES;
|
2019-08-29 04:49:06 +02:00
|
|
|
}
|
2019-08-28 07:18:42 +02:00
|
|
|
}
|
|
|
|
|
2021-01-21 04:49:50 +01:00
|
|
|
- (BOOL)userHasModerationPermission
|
|
|
|
{
|
|
|
|
if (!self.isGroupThread) return false;
|
|
|
|
TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread;
|
|
|
|
|
|
|
|
// Make sure it's an open group message
|
|
|
|
TSMessage *message = (TSMessage *)self.interaction;
|
|
|
|
if (!message.isOpenGroupMessage) return false;
|
|
|
|
|
|
|
|
// Ensure we have the details needed to contact the server
|
2021-03-24 04:36:26 +01:00
|
|
|
SNOpenGroupV2 *openGroupV2 = [LKStorage.shared getV2OpenGroupForThreadID:groupThread.uniqueId];
|
2021-05-05 06:22:29 +02:00
|
|
|
if (openGroupV2 == nil) return false;
|
2021-01-21 04:49:50 +01:00
|
|
|
|
|
|
|
// Check that we're a moderator
|
2021-03-24 04:36:26 +01:00
|
|
|
if (openGroupV2 != nil) {
|
|
|
|
return [SNOpenGroupAPIV2 isUserModerator:[SNGeneralUtilities getUserPublicKey] forRoom:openGroupV2.room onServer:openGroupV2.server];
|
|
|
|
}
|
2021-01-21 04:49:50 +01:00
|
|
|
}
|
|
|
|
|
2017-10-10 22:13:54 +02:00
|
|
|
@end
|
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|