diff --git a/Signal/src/Loki/LokiGroupChatPoller.swift b/Signal/src/Loki/LokiGroupChatPoller.swift index 3f0fce66f..767a01c9c 100644 --- a/Signal/src/Loki/LokiGroupChatPoller.swift +++ b/Signal/src/Loki/LokiGroupChatPoller.swift @@ -91,8 +91,14 @@ public final class LokiGroupChatPoller : NSObject { guard !isDuplicate else { return } guard let groupID = group.id.data(using: .utf8) else { return } let thread = TSGroupThread.getOrCreateThread(withGroupId: groupID) + let signalQuote: TSQuotedMessage? + if let quote = message.quote { + signalQuote = TSQuotedMessage(timestamp: quote.quotedMessageTimestamp, authorId: quote.quoteeHexEncodedPublicKey, body: quote.quotedMessageBody, quotedAttachmentsForSending: []) + } else { + signalQuote = nil + } let message = TSOutgoingMessage(outgoingMessageWithTimestamp: message.timestamp, in: thread, messageBody: message.body, attachmentIds: [], expiresInSeconds: 0, - expireStartedAt: 0, isVoiceMessage: false, groupMetaMessage: .deliver, quotedMessage: nil, contactShare: nil, linkPreview: nil) + expireStartedAt: 0, isVoiceMessage: false, groupMetaMessage: .deliver, quotedMessage: signalQuote, contactShare: nil, linkPreview: nil) storage.dbReadWriteConnection.readWrite { transaction in message.update(withSentRecipient: group.server, wasSentByUD: false, transaction: transaction) message.saveGroupChatMessageID(messageServerID, in: transaction) diff --git a/Signal/src/Models/MessageActions.swift b/Signal/src/Models/MessageActions.swift index b190c05b7..044efd4ec 100644 --- a/Signal/src/Models/MessageActions.swift +++ b/Signal/src/Models/MessageActions.swift @@ -74,9 +74,10 @@ class ConversationViewItemActions: NSObject { class func textActions(conversationViewItem: ConversationViewItem, shouldAllowReply: Bool, delegate: MessageActionsDelegate) -> [MenuAction] { var actions: [MenuAction] = [] - let isGroup = conversationViewItem.isGroupThread + let isGroup = conversationViewItem.isGroupThread; + let isRSSFeed = conversationViewItem.isRSSFeed; - if shouldAllowReply && !isGroup { + if shouldAllowReply && !isRSSFeed { let replyAction = MessageActionBuilder.reply(conversationViewItem: conversationViewItem, delegate: delegate) actions.append(replyAction) } diff --git a/Signal/src/ViewControllers/ColorPickerViewController.swift b/Signal/src/ViewControllers/ColorPickerViewController.swift index f82137efd..1dd91f8cc 100644 --- a/Signal/src/ViewControllers/ColorPickerViewController.swift +++ b/Signal/src/ViewControllers/ColorPickerViewController.swift @@ -306,6 +306,7 @@ class ColorPickerView: UIView, ColorViewDelegate { @objc private class MockConversationViewItem: NSObject, ConversationViewItem { var userCanDeleteGroupMessage: Bool = false + var isRSSFeed: Bool = false var interaction: TSInteraction = TSMessage() var interactionType: OWSInteractionType = OWSInteractionType.unknown var quotedReply: OWSQuotedReplyModel? diff --git a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m index 4b9677630..aebc4db13 100644 --- a/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m +++ b/Signal/src/ViewControllers/ConversationView/Cells/OWSQuotedMessageView.m @@ -361,7 +361,7 @@ const CGFloat kRemotelySourcedContentRowSpacing = 3; OWSAssertDebug(CGSizeEqualToSize( CGSizeMake(kRemotelySourcedContentGlyphLength, kRemotelySourcedContentGlyphLength), glyphImage.size)); UIImageView *glyphView = [[UIImageView alloc] initWithImage:glyphImage]; - glyphView.tintColor = Theme.secondaryColor; + glyphView.tintColor = UIColor.whiteColor; [glyphView autoSetDimensionsToSize:CGSizeMake(kRemotelySourcedContentGlyphLength, kRemotelySourcedContentGlyphLength)]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 79d855cca..8181d7c8c 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -513,7 +513,7 @@ typedef enum : NSUInteger { _conversationStyle = [[ConversationStyle alloc] initWithThread:thread]; _conversationViewModel = - [[ConversationViewModel alloc] initWithThread:thread focusMessageIdOnOpen:focusMessageId delegate:self]; + [[ConversationViewModel alloc] initWithThread:thread focusMessageIdOnOpen:focusMessageId isRSSFeed:self.isRSSFeed delegate:self]; _searchController = [[ConversationSearchController alloc] initWithThread:thread]; _searchController.delegate = self; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h index 6504d0ef9..0ced1b753 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.h @@ -67,6 +67,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly, nullable) OWSQuotedReplyModel *quotedReply; @property (nonatomic, readonly) BOOL isGroupThread; +@property (nonatomic, readonly) BOOL isRSSFeed; @property (nonatomic, readonly) BOOL userCanDeleteGroupMessage; @property (nonatomic, readonly) BOOL hasBodyText; @@ -162,6 +163,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithInteraction:(TSInteraction *)interaction isGroupThread:(BOOL)isGroupThread + isRSSFeed:(BOOL)isRSSFeed transaction:(YapDatabaseReadTransaction *)transaction conversationStyle:(ConversationStyle *)conversationStyle; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m index f4de057f7..e3a575d1c 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewItem.m @@ -119,6 +119,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) @synthesize interaction = _interaction; @synthesize isFirstInCluster = _isFirstInCluster; @synthesize isGroupThread = _isGroupThread; +@synthesize isRSSFeed = _isRSSFeed; @synthesize isLastInCluster = _isLastInCluster; @synthesize lastAudioMessageView = _lastAudioMessageView; @synthesize senderName = _senderName; @@ -126,6 +127,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) - (instancetype)initWithInteraction:(TSInteraction *)interaction isGroupThread:(BOOL)isGroupThread + isRSSFeed:(BOOL)isRSSFeed transaction:(YapDatabaseReadTransaction *)transaction conversationStyle:(ConversationStyle *)conversationStyle { @@ -141,6 +143,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) _interaction = interaction; _isGroupThread = isGroupThread; + _isRSSFeed = isRSSFeed; _conversationStyle = conversationStyle; [self updateAuthorConversationColorNameWithTransaction:transaction]; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h index a4025f1f7..1b9c4d86a 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.h @@ -98,10 +98,12 @@ typedef NS_ENUM(NSUInteger, ConversationUpdateItemType) { @property (nonatomic, readonly) ConversationViewState *viewState; @property (nonatomic, nullable) NSString *focusMessageIdOnOpen; @property (nonatomic, readonly, nullable) ThreadDynamicInteractions *dynamicInteractions; +@property (nonatomic, readonly) BOOL isRSSFeed; - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithThread:(TSThread *)thread focusMessageIdOnOpen:(nullable NSString *)focusMessageIdOnOpen + isRSSFeed:(BOOL)isRSSFeed delegate:(id)delegate NS_DESIGNATED_INITIALIZER; - (void)ensureDynamicInteractionsAndUpdateIfNecessary:(BOOL)updateIfNecessary; diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m index e9162562e..2eb7fdfaa 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewModel.m @@ -218,6 +218,7 @@ static const int kYapDatabaseRangeMaxLength = 25000; - (instancetype)initWithThread:(TSThread *)thread focusMessageIdOnOpen:(nullable NSString *)focusMessageIdOnOpen + isRSSFeed:(BOOL)isRSSFeed delegate:(id)delegate { self = [super init]; @@ -233,6 +234,7 @@ static const int kYapDatabaseRangeMaxLength = 25000; _persistedViewItems = @[]; _unsavedOutgoingMessages = @[]; self.focusMessageIdOnOpen = focusMessageIdOnOpen; + _isRSSFeed = isRSSFeed; _viewState = [[ConversationViewState alloc] initWithViewItems:@[]]; [self configure]; @@ -1205,6 +1207,7 @@ static const int kYapDatabaseRangeMaxLength = 25000; NSArray *loadedUniqueIds = [self.messageMapping loadedUniqueIds]; BOOL isGroupThread = self.thread.isGroupThread; + BOOL isRSSFeed = self.isRSSFeed; ConversationStyle *conversationStyle = self.delegate.conversationStyle; [self ensureConversationProfileState]; @@ -1218,6 +1221,7 @@ static const int kYapDatabaseRangeMaxLength = 25000; if (!viewItem) { viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:interaction isGroupThread:isGroupThread + isRSSFeed:isRSSFeed transaction:transaction conversationStyle:conversationStyle]; } diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m index 90fd5b4c4..a1e358a07 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIMessages.m @@ -2012,6 +2012,7 @@ NS_ASSUME_NONNULL_BEGIN id viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:messageToQuote isGroupThread:thread.isGroupThread + isRSSFeed:NO transaction:transaction conversationStyle:conversationStyle]; quotedMessage = [ @@ -2033,6 +2034,7 @@ NS_ASSUME_NONNULL_BEGIN id viewItem = [[ConversationInteractionViewItem alloc] initWithInteraction:messageToQuote isGroupThread:thread.isGroupThread + isRSSFeed:NO transaction:transaction conversationStyle:conversationStyle]; quotedMessage = [ diff --git a/Signal/src/ViewControllers/MediaPageViewController.swift b/Signal/src/ViewControllers/MediaPageViewController.swift index e403c99d1..1c541676e 100644 --- a/Signal/src/ViewControllers/MediaPageViewController.swift +++ b/Signal/src/ViewControllers/MediaPageViewController.swift @@ -579,6 +579,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou let conversationStyle = ConversationStyle(thread: thread) fetchedItem = ConversationInteractionViewItem(interaction: message, isGroupThread: thread.isGroupThread(), + isRSSFeed: false, transaction: transaction, conversationStyle: conversationStyle) } diff --git a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift index 8e59d42e4..3bc8e638d 100644 --- a/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift +++ b/SignalServiceKit/src/Loki/API/LokiGroupChatAPI.swift @@ -178,8 +178,7 @@ public final class LokiGroupChatAPI : NSObject { throw Error.parsingFailed } let timestamp = UInt64(date.timeIntervalSince1970) * 1000 - let quote: LokiGroupMessage.Quote? - return LokiGroupMessage(serverID: serverID, hexEncodedPublicKey: userHexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp, quote: nil) + return LokiGroupMessage(serverID: serverID, hexEncodedPublicKey: userHexEncodedPublicKey, displayName: displayName, body: body, type: publicChatMessageType, timestamp: timestamp, quote: message.quote) } }.recover { error -> Promise in if let error = error as? NetworkManagerError, error.statusCode == 401 { diff --git a/SignalServiceKit/src/Loki/API/LokiGroupMessage.swift b/SignalServiceKit/src/Loki/API/LokiGroupMessage.swift index 7b90bb092..f972039b8 100644 --- a/SignalServiceKit/src/Loki/API/LokiGroupMessage.swift +++ b/SignalServiceKit/src/Loki/API/LokiGroupMessage.swift @@ -31,12 +31,21 @@ public final class LokiGroupMessage : NSObject { super.init() } - @objc public convenience init(hexEncodedPublicKey: String, displayName: String, body: String, type: String, timestamp: UInt64) { - self.init(serverID: nil, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: type, timestamp: timestamp, quote: nil) + @objc public convenience init(hexEncodedPublicKey: String, displayName: String, body: String, type: String, timestamp: UInt64, quotedMessageTimestamp: UInt64, quoteeHexEncodedPublicKey: String?, quotedMessageBody: String?) { + let quote: Quote? + if quotedMessageTimestamp != 0, let quoteeHexEncodedPublicKey = quoteeHexEncodedPublicKey, let quotedMessageBody = quotedMessageBody { + quote = Quote(quotedMessageTimestamp: quotedMessageTimestamp, quoteeHexEncodedPublicKey: quoteeHexEncodedPublicKey, quotedMessageBody: quotedMessageBody) + } else { + quote = nil + } + self.init(serverID: nil, hexEncodedPublicKey: hexEncodedPublicKey, displayName: displayName, body: body, type: type, timestamp: timestamp, quote: quote) } internal func toJSON() -> JSON { - let value: JSON = [ "timestamp" : timestamp, "from" : displayName, "source" : hexEncodedPublicKey ] + var value: JSON = [ "timestamp" : timestamp, "from" : displayName, "source" : hexEncodedPublicKey ] + if let quote = quote { + value["quote"] = [ "id" : quote.quotedMessageTimestamp, "author" : quote.quoteeHexEncodedPublicKey, "text" : quote.quotedMessageBody ] + } return [ "text" : body, "annotations": [ [ "type" : type, "value" : value ] ] ] } } diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m index 3c7be02cc..ebde64588 100644 --- a/SignalServiceKit/src/Messages/OWSMessageSender.m +++ b/SignalServiceKit/src/Messages/OWSMessageSender.m @@ -1113,7 +1113,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; NSString *userHexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; NSString *displayName = SSKEnvironment.shared.profileManager.localProfileName; if (displayName == nil) { displayName = @"Anonymous"; } - LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userHexEncodedPublicKey displayName:displayName body:message.body type:LKGroupChatAPI.publicChatMessageType timestamp:message.timestamp]; + TSQuotedMessage *quote = message.quotedMessage; + LKGroupMessage *groupMessage = [[LKGroupMessage alloc] initWithHexEncodedPublicKey:userHexEncodedPublicKey displayName:displayName body:message.body type:LKGroupChatAPI.publicChatMessageType + timestamp:message.timestamp quotedMessageTimestamp:quote.timestamp quoteeHexEncodedPublicKey:quote.authorId quotedMessageBody:quote.body]; [[LKGroupChatAPI sendMessage:groupMessage toGroup:LKGroupChatAPI.publicChatServerID onServer:LKGroupChatAPI.publicChatServer] .thenOn(OWSDispatch.sendingQueue, ^(LKGroupMessage *groupMessage) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {