2017-02-07 21:09:04 +01:00
|
|
|
//
|
2018-01-26 22:11:05 +01:00
|
|
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
2017-02-07 21:09:04 +01:00
|
|
|
//
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
#import "TSThread.h"
|
2018-04-18 02:53:27 +02:00
|
|
|
#import "NSDate+OWS.h"
|
2018-04-21 16:25:13 +02:00
|
|
|
#import "NSString+SSK.h"
|
2018-03-05 15:30:58 +01:00
|
|
|
#import "OWSPrimaryStorage.h"
|
2017-02-07 21:09:04 +01:00
|
|
|
#import "OWSReadTracking.h"
|
2016-07-28 01:58:49 +02:00
|
|
|
#import "TSDatabaseView.h"
|
2015-12-07 03:31:43 +01:00
|
|
|
#import "TSIncomingMessage.h"
|
2017-06-10 20:13:23 +02:00
|
|
|
#import "TSInfoMessage.h"
|
2016-07-28 01:58:49 +02:00
|
|
|
#import "TSInteraction.h"
|
2016-08-22 22:09:58 +02:00
|
|
|
#import "TSInvalidIdentityKeyReceivingErrorMessage.h"
|
2015-12-07 03:31:43 +01:00
|
|
|
#import "TSOutgoingMessage.h"
|
2017-04-17 21:02:53 +02:00
|
|
|
#import <YapDatabase/YapDatabase.h>
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2016-08-23 22:38:05 +02:00
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
@interface TSThread ()
|
|
|
|
|
2017-04-17 21:02:53 +02:00
|
|
|
@property (nonatomic) NSDate *creationDate;
|
2015-12-07 03:31:43 +01:00
|
|
|
@property (nonatomic, copy) NSDate *archivalDate;
|
2017-04-17 21:02:53 +02:00
|
|
|
@property (nonatomic) NSDate *lastMessageDate;
|
2015-12-07 03:31:43 +01:00
|
|
|
@property (nonatomic, copy) NSString *messageDraft;
|
2017-04-19 15:59:49 +02:00
|
|
|
@property (atomic, nullable) NSDate *mutedUntilDate;
|
2016-04-08 18:59:05 +02:00
|
|
|
|
2018-04-21 17:12:58 +02:00
|
|
|
- (TSInteraction *)lastInteractionWithTranscation:(YapDatabaseReadTransaction *)transaction;
|
2016-04-08 18:59:05 +02:00
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation TSThread
|
|
|
|
|
|
|
|
+ (NSString *)collection {
|
|
|
|
return @"TSThread";
|
|
|
|
}
|
|
|
|
|
2017-11-15 19:15:48 +01:00
|
|
|
- (instancetype)initWithUniqueId:(NSString *_Nullable)uniqueId
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
self = [super initWithUniqueId:uniqueId];
|
|
|
|
|
|
|
|
if (self) {
|
|
|
|
_archivalDate = nil;
|
|
|
|
_lastMessageDate = nil;
|
|
|
|
_creationDate = [NSDate date];
|
|
|
|
_messageDraft = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2016-07-28 04:29:27 +02:00
|
|
|
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
|
|
{
|
2018-03-28 16:42:48 +02:00
|
|
|
[self removeAllThreadInteractionsWithTransaction:transaction];
|
|
|
|
|
2016-07-28 04:29:27 +02:00
|
|
|
[super removeWithTransaction:transaction];
|
2018-03-28 16:42:48 +02:00
|
|
|
}
|
2016-07-28 04:29:27 +02:00
|
|
|
|
2018-03-28 16:42:48 +02:00
|
|
|
- (void)removeAllThreadInteractionsWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
|
|
{
|
2017-07-27 19:02:42 +02:00
|
|
|
// We can't safely delete interactions while enumerating them, so
|
|
|
|
// we collect and delete separately.
|
|
|
|
//
|
|
|
|
// We don't want to instantiate the interactions when collecting them
|
|
|
|
// or when deleting them.
|
|
|
|
NSMutableArray<NSString *> *interactionIds = [NSMutableArray new];
|
|
|
|
YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
|
|
|
|
OWSAssert(interactionsByThread);
|
|
|
|
[interactionsByThread
|
|
|
|
enumerateKeysInGroup:self.uniqueId
|
|
|
|
usingBlock:^(
|
|
|
|
NSString *_Nonnull collection, NSString *_Nonnull key, NSUInteger index, BOOL *_Nonnull stop) {
|
|
|
|
[interactionIds addObject:key];
|
|
|
|
}];
|
2016-07-28 04:29:27 +02:00
|
|
|
|
|
|
|
for (NSString *interactionId in interactionIds) {
|
2018-03-20 18:02:10 +01:00
|
|
|
// We need to fetch each interaction, since [TSInteraction removeWithTransaction:] does important work.
|
|
|
|
TSInteraction *_Nullable interaction =
|
|
|
|
[TSInteraction fetchObjectWithUniqueID:interactionId transaction:transaction];
|
|
|
|
if (!interaction) {
|
|
|
|
OWSProdLogAndFail(@"%@ couldn't load thread's interaction for deletion.", self.logTag);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
[interaction removeWithTransaction:transaction];
|
2016-07-28 04:29:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
#pragma mark To be subclassed.
|
|
|
|
|
|
|
|
- (BOOL)isGroupThread {
|
2017-12-19 03:17:11 +01:00
|
|
|
OWS_ABSTRACT_METHOD();
|
|
|
|
|
|
|
|
return NO;
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2016-08-23 22:38:05 +02:00
|
|
|
// Override in ContactThread
|
|
|
|
- (nullable NSString *)contactIdentifier
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
- (NSString *)name {
|
2017-12-19 03:17:11 +01:00
|
|
|
OWS_ABSTRACT_METHOD();
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2017-05-24 22:43:18 +02:00
|
|
|
- (NSArray<NSString *> *)recipientIdentifiers
|
|
|
|
{
|
2017-12-19 03:17:11 +01:00
|
|
|
OWS_ABSTRACT_METHOD();
|
|
|
|
|
2017-05-24 22:43:18 +02:00
|
|
|
return @[];
|
|
|
|
}
|
|
|
|
|
2016-08-23 22:38:05 +02:00
|
|
|
- (nullable UIImage *)image
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2016-11-08 01:23:52 +01:00
|
|
|
- (BOOL)hasSafetyNumbers
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2016-07-28 04:29:27 +02:00
|
|
|
#pragma mark Interactions
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Iterate over this thread's interactions
|
|
|
|
*/
|
|
|
|
- (void)enumerateInteractionsWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
|
|
usingBlock:(void (^)(TSInteraction *interaction,
|
|
|
|
YapDatabaseReadTransaction *transaction))block
|
|
|
|
{
|
|
|
|
void (^interactionBlock)(NSString *, NSString *, id, id, NSUInteger, BOOL *) = ^void(NSString *_Nonnull collection,
|
|
|
|
NSString *_Nonnull key,
|
|
|
|
id _Nonnull object,
|
|
|
|
id _Nonnull metadata,
|
|
|
|
NSUInteger index,
|
|
|
|
BOOL *_Nonnull stop) {
|
|
|
|
|
|
|
|
TSInteraction *interaction = object;
|
|
|
|
block(interaction, transaction);
|
|
|
|
};
|
|
|
|
|
|
|
|
YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
|
|
|
|
[interactionsByThread enumerateRowsInGroup:self.uniqueId usingBlock:interactionBlock];
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
/**
|
|
|
|
* Enumerates all the threads interactions. Note this will explode if you try to create a transaction in the block.
|
|
|
|
* If you need a transaction, use the sister method: `enumerateInteractionsWithTransaction:usingBlock`
|
|
|
|
*/
|
|
|
|
- (void)enumerateInteractionsUsingBlock:(void (^)(TSInteraction *interaction))block
|
|
|
|
{
|
2017-07-05 22:58:39 +02:00
|
|
|
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
2016-08-22 22:09:58 +02:00
|
|
|
[self enumerateInteractionsWithTransaction:transaction
|
|
|
|
usingBlock:^(
|
|
|
|
TSInteraction *interaction, YapDatabaseReadTransaction *transaction) {
|
|
|
|
|
|
|
|
block(interaction);
|
|
|
|
}];
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2016-09-20 03:57:20 +02:00
|
|
|
/**
|
|
|
|
* Useful for tests and debugging. In production use an enumeration method.
|
|
|
|
*/
|
|
|
|
- (NSArray<TSInteraction *> *)allInteractions
|
|
|
|
{
|
|
|
|
NSMutableArray<TSInteraction *> *interactions = [NSMutableArray new];
|
|
|
|
[self enumerateInteractionsUsingBlock:^(TSInteraction *_Nonnull interaction) {
|
|
|
|
[interactions addObject:interaction];
|
|
|
|
}];
|
|
|
|
|
|
|
|
return [interactions copy];
|
|
|
|
}
|
|
|
|
|
2016-08-22 22:09:58 +02:00
|
|
|
- (NSArray<TSInvalidIdentityKeyReceivingErrorMessage *> *)receivedMessagesForInvalidKey:(NSData *)key
|
|
|
|
{
|
|
|
|
NSMutableArray *errorMessages = [NSMutableArray new];
|
|
|
|
[self enumerateInteractionsUsingBlock:^(TSInteraction *interaction) {
|
|
|
|
if ([interaction isKindOfClass:[TSInvalidIdentityKeyReceivingErrorMessage class]]) {
|
|
|
|
TSInvalidIdentityKeyReceivingErrorMessage *error = (TSInvalidIdentityKeyReceivingErrorMessage *)interaction;
|
|
|
|
if ([[error newIdentityKey] isEqualToData:key]) {
|
|
|
|
[errorMessages addObject:(TSInvalidIdentityKeyReceivingErrorMessage *)interaction];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
|
|
|
|
return [errorMessages copy];
|
|
|
|
}
|
|
|
|
|
2016-07-28 04:29:27 +02:00
|
|
|
- (NSUInteger)numberOfInteractions
|
|
|
|
{
|
|
|
|
__block NSUInteger count;
|
2017-07-05 22:58:39 +02:00
|
|
|
[[self dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
2016-07-28 04:29:27 +02:00
|
|
|
YapDatabaseViewTransaction *interactionsByThread = [transaction ext:TSMessageDatabaseViewExtensionName];
|
|
|
|
count = [interactionsByThread numberOfItemsInGroup:self.uniqueId];
|
|
|
|
}];
|
|
|
|
return count;
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
|
2018-04-21 17:12:58 +02:00
|
|
|
- (BOOL)hasUnreadMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
|
|
|
{
|
|
|
|
TSInteraction *interaction = [self lastInteractionWithTransaction:transaction];
|
2016-04-08 18:59:05 +02:00
|
|
|
BOOL hasUnread = NO;
|
|
|
|
|
|
|
|
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
|
|
|
|
hasUnread = ![(TSIncomingMessage *)interaction wasRead];
|
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
|
|
|
|
return hasUnread;
|
|
|
|
}
|
|
|
|
|
2017-05-25 18:17:45 +02:00
|
|
|
- (NSArray<id<OWSReadTracking>> *)unseenMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
|
|
|
{
|
|
|
|
NSMutableArray<id<OWSReadTracking>> *messages = [NSMutableArray new];
|
2017-06-15 19:37:10 +02:00
|
|
|
[[TSDatabaseView unseenDatabaseViewExtension:transaction]
|
2017-05-25 18:17:45 +02:00
|
|
|
enumerateRowsInGroup:self.uniqueId
|
|
|
|
usingBlock:^(
|
|
|
|
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
|
|
|
|
|
|
|
|
if (![object conformsToProtocol:@protocol(OWSReadTracking)]) {
|
2017-11-08 20:04:51 +01:00
|
|
|
OWSFail(@"%@ Unexpected object in unseen messages: %@", self.logTag, object);
|
2017-05-26 16:32:13 +02:00
|
|
|
return;
|
2017-05-25 18:17:45 +02:00
|
|
|
}
|
|
|
|
[messages addObject:(id<OWSReadTracking>)object];
|
|
|
|
}];
|
|
|
|
|
|
|
|
return [messages copy];
|
|
|
|
}
|
|
|
|
|
2018-04-21 17:12:58 +02:00
|
|
|
- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction
|
|
|
|
{
|
|
|
|
return [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:self.uniqueId];
|
|
|
|
}
|
|
|
|
|
2016-10-05 17:42:44 +02:00
|
|
|
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
|
|
|
{
|
2017-05-25 18:17:45 +02:00
|
|
|
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {
|
2018-04-18 02:53:27 +02:00
|
|
|
[message markAsReadAtTimestamp:[NSDate ows_millisecondTimeStamp] sendReadReceipt:YES transaction:transaction];
|
2016-10-05 17:42:44 +02:00
|
|
|
}
|
2017-05-25 18:17:45 +02:00
|
|
|
|
|
|
|
// Just to be defensive, we'll also check for unread messages.
|
2017-05-30 17:01:54 +02:00
|
|
|
OWSAssert([self unseenMessagesWithTransaction:transaction].count < 1);
|
2016-10-05 17:42:44 +02:00
|
|
|
}
|
|
|
|
|
2018-04-21 17:12:58 +02:00
|
|
|
- (TSInteraction *)lastInteractionWithTransaction:(YapDatabaseReadTransaction *)transaction
|
|
|
|
{
|
|
|
|
return [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId];
|
2016-04-08 18:59:05 +02:00
|
|
|
}
|
|
|
|
|
2018-04-21 16:25:13 +02:00
|
|
|
- (TSInteraction *)lastInteractionForInboxWithTransaction:(YapDatabaseReadTransaction *)transaction
|
2017-06-12 19:30:04 +02:00
|
|
|
{
|
2017-06-14 15:50:36 +02:00
|
|
|
__block TSInteraction *last = nil;
|
2018-04-21 16:25:13 +02:00
|
|
|
[[transaction ext:TSMessageDatabaseViewExtensionName]
|
|
|
|
enumerateRowsInGroup:self.uniqueId
|
|
|
|
withOptions:NSEnumerationReverse
|
|
|
|
usingBlock:^(
|
|
|
|
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
|
|
|
|
|
|
|
|
OWSAssert([object isKindOfClass:[TSInteraction class]]);
|
|
|
|
|
|
|
|
TSInteraction *interaction = (TSInteraction *)object;
|
|
|
|
|
|
|
|
if ([TSThread shouldInteractionAppearInInbox:interaction]) {
|
|
|
|
last = interaction;
|
|
|
|
*stop = YES;
|
|
|
|
}
|
|
|
|
}];
|
2017-06-12 19:33:12 +02:00
|
|
|
return last;
|
2017-06-12 19:30:04 +02:00
|
|
|
}
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
- (NSDate *)lastMessageDate {
|
|
|
|
if (_lastMessageDate) {
|
|
|
|
return _lastMessageDate;
|
|
|
|
} else {
|
|
|
|
return _creationDate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-21 17:12:58 +02:00
|
|
|
- (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
2018-04-21 16:25:13 +02:00
|
|
|
{
|
|
|
|
TSInteraction *interaction = [self lastInteractionForInboxWithTransaction:transaction];
|
|
|
|
if ([interaction conformsToProtocol:@protocol(OWSPreviewText)]) {
|
|
|
|
id<OWSPreviewText> previewable = (id<OWSPreviewText>)interaction;
|
|
|
|
return [previewable previewTextWithTransaction:transaction].filterStringForDisplay;
|
2016-04-08 18:59:05 +02:00
|
|
|
} else {
|
2018-04-21 16:25:13 +02:00
|
|
|
return @"";
|
2016-04-08 18:59:05 +02:00
|
|
|
}
|
2015-12-07 03:31:43 +01:00
|
|
|
}
|
|
|
|
|
2017-06-15 21:19:55 +02:00
|
|
|
// Returns YES IFF the interaction should show up in the inbox as the last message.
|
2017-06-12 19:30:04 +02:00
|
|
|
+ (BOOL)shouldInteractionAppearInInbox:(TSInteraction *)interaction
|
|
|
|
{
|
|
|
|
OWSAssert(interaction);
|
|
|
|
|
|
|
|
if (interaction.isDynamicInteraction) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([interaction isKindOfClass:[TSErrorMessage class]]) {
|
|
|
|
TSErrorMessage *errorMessage = (TSErrorMessage *)interaction;
|
|
|
|
if (errorMessage.errorType == TSErrorMessageNonBlockingIdentityChange) {
|
|
|
|
// Otherwise all group threads with the recipient will percolate to the top of the inbox, even though
|
|
|
|
// there was no meaningful interaction.
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
} else if ([interaction isKindOfClass:[TSInfoMessage class]]) {
|
|
|
|
TSInfoMessage *infoMessage = (TSInfoMessage *)interaction;
|
|
|
|
if (infoMessage.messageType == TSInfoMessageVerificationStateChange) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
- (void)updateWithLastMessage:(TSInteraction *)lastMessage transaction:(YapDatabaseReadWriteTransaction *)transaction {
|
2017-06-10 20:13:23 +02:00
|
|
|
OWSAssert(lastMessage);
|
|
|
|
OWSAssert(transaction);
|
|
|
|
|
2017-06-12 19:30:04 +02:00
|
|
|
if (![self.class shouldInteractionAppearInInbox:lastMessage]) {
|
2017-06-10 20:13:23 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-02 20:18:27 +02:00
|
|
|
self.hasEverHadMessage = YES;
|
|
|
|
|
2017-05-25 18:17:45 +02:00
|
|
|
NSDate *lastMessageDate = [lastMessage dateForSorting];
|
2015-12-07 03:31:43 +01:00
|
|
|
if (!_lastMessageDate || [lastMessageDate timeIntervalSinceDate:self.lastMessageDate] > 0) {
|
|
|
|
_lastMessageDate = lastMessageDate;
|
|
|
|
|
|
|
|
[self saveWithTransaction:transaction];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark Archival
|
|
|
|
|
2016-08-23 22:38:05 +02:00
|
|
|
- (nullable NSDate *)archivalDate
|
|
|
|
{
|
2015-12-07 03:31:43 +01:00
|
|
|
return _archivalDate;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction {
|
|
|
|
[self archiveThreadWithTransaction:transaction referenceDate:[NSDate date]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)archiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction referenceDate:(NSDate *)date {
|
|
|
|
[self markAllAsReadWithTransaction:transaction];
|
|
|
|
_archivalDate = date;
|
|
|
|
|
|
|
|
[self saveWithTransaction:transaction];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)unarchiveThreadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction {
|
|
|
|
_archivalDate = nil;
|
|
|
|
[self saveWithTransaction:transaction];
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma mark Drafts
|
|
|
|
|
|
|
|
- (NSString *)currentDraftWithTransaction:(YapDatabaseReadTransaction *)transaction {
|
|
|
|
TSThread *thread = [TSThread fetchObjectWithUniqueID:self.uniqueId transaction:transaction];
|
|
|
|
if (thread.messageDraft) {
|
|
|
|
return thread.messageDraft;
|
|
|
|
} else {
|
|
|
|
return @"";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setDraft:(NSString *)draftString transaction:(YapDatabaseReadWriteTransaction *)transaction {
|
|
|
|
TSThread *thread = [TSThread fetchObjectWithUniqueID:self.uniqueId transaction:transaction];
|
|
|
|
thread.messageDraft = draftString;
|
|
|
|
[thread saveWithTransaction:transaction];
|
|
|
|
}
|
|
|
|
|
2017-04-17 21:02:53 +02:00
|
|
|
#pragma mark - Muted
|
|
|
|
|
|
|
|
- (BOOL)isMuted
|
|
|
|
{
|
|
|
|
NSDate *mutedUntilDate = self.mutedUntilDate;
|
|
|
|
NSDate *now = [NSDate date];
|
|
|
|
return (mutedUntilDate != nil &&
|
|
|
|
[mutedUntilDate timeIntervalSinceDate:now] > 0);
|
|
|
|
}
|
|
|
|
|
2017-11-15 19:15:48 +01:00
|
|
|
#pragma mark - Update With... Methods
|
2017-04-17 21:02:53 +02:00
|
|
|
|
|
|
|
- (void)updateWithMutedUntilDate:(NSDate *)mutedUntilDate
|
|
|
|
{
|
2017-07-05 22:58:39 +02:00
|
|
|
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
2017-11-15 19:15:48 +01:00
|
|
|
[self applyChangeToSelfAndLatestCopy:transaction
|
|
|
|
changeBlock:^(TSThread *thread) {
|
|
|
|
[thread setMutedUntilDate:mutedUntilDate];
|
|
|
|
}];
|
2017-04-17 21:02:53 +02:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2015-12-07 03:31:43 +01:00
|
|
|
@end
|
2016-08-23 22:38:05 +02:00
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|