mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Merge branch 'mkirk/unjank-homescreen'
This commit is contained in:
commit
e66e5b0b97
|
@ -302,6 +302,7 @@
|
|||
45360B901F9527DA00FA666C /* SearcherTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45360B8F1F9527DA00FA666C /* SearcherTest.swift */; };
|
||||
45360B911F952AA900FA666C /* MarqueeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */; };
|
||||
4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4539B5851F79348F007141FF /* PushRegistrationManager.swift */; };
|
||||
4542DF52208B82E9007B4E76 /* ThreadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadModel.swift */; };
|
||||
45464DBC1DFA041F001D3FD6 /* DataChannelMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */; };
|
||||
454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 454A84032059C787008B8C75 /* MediaTileViewController.swift */; };
|
||||
454A965A1FD6017E008D2A0E /* SignalAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D913491F62D4A500722898 /* SignalAttachment.swift */; };
|
||||
|
@ -919,6 +920,7 @@
|
|||
45360B8F1F9527DA00FA666C /* SearcherTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearcherTest.swift; sourceTree = "<group>"; };
|
||||
4539B5851F79348F007141FF /* PushRegistrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushRegistrationManager.swift; sourceTree = "<group>"; };
|
||||
453CC0361D08E1A60040EBA3 /* sn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sn; path = translations/sn.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
4542DF51208B82E9007B4E76 /* ThreadModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadModel.swift; sourceTree = "<group>"; };
|
||||
45464DBB1DFA041F001D3FD6 /* DataChannelMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataChannelMessage.swift; sourceTree = "<group>"; };
|
||||
454A84032059C787008B8C75 /* MediaTileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaTileViewController.swift; sourceTree = "<group>"; };
|
||||
454A965E1FD60EA2008D2A0E /* OWSFlatButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSFlatButton.swift; path = SignalMessaging/Views/OWSFlatButton.swift; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -1898,6 +1900,7 @@
|
|||
45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */,
|
||||
458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */,
|
||||
458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */,
|
||||
4542DF51208B82E9007B4E76 /* ThreadModel.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3199,6 +3202,7 @@
|
|||
34D1F0501F7D45A60066283D /* GifPickerCell.swift in Sources */,
|
||||
34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */,
|
||||
340FC8B8204DAC8D007AEB0F /* AddToGroupViewController.m in Sources */,
|
||||
4542DF52208B82E9007B4E76 /* ThreadModel.swift in Sources */,
|
||||
341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */,
|
||||
340FC8AF204DAC8D007AEB0F /* OWSLinkDeviceViewController.m in Sources */,
|
||||
34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */,
|
||||
|
|
40
Signal/src/Models/ThreadModel.swift
Normal file
40
Signal/src/Models/ThreadModel.swift
Normal file
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
public class ThreadViewModel: NSObject {
|
||||
let hasUnreadMessages: Bool
|
||||
let lastMessageDate: Date
|
||||
let isGroupThread: Bool
|
||||
let threadRecord: TSThread
|
||||
let unreadCount: UInt
|
||||
let contactIdentifier: String?
|
||||
let name: String
|
||||
let isMuted: Bool
|
||||
var isContactThread: Bool {
|
||||
return !isGroupThread
|
||||
}
|
||||
|
||||
let lastMessageText: String?
|
||||
|
||||
init(thread: TSThread, transaction: YapDatabaseReadTransaction) {
|
||||
self.threadRecord = thread
|
||||
self.lastMessageDate = thread.lastMessageDate()
|
||||
self.isGroupThread = thread.isGroupThread()
|
||||
self.name = thread.name()
|
||||
self.isMuted = thread.isMuted
|
||||
self.lastMessageText = thread.lastMessageText(transaction: transaction)
|
||||
|
||||
if let contactThread = thread as? TSContactThread {
|
||||
self.contactIdentifier = contactThread.contactIdentifier()
|
||||
} else {
|
||||
self.contactIdentifier = nil
|
||||
}
|
||||
|
||||
self.unreadCount = thread.unreadMessageCount(transaction: transaction)
|
||||
self.hasUnreadMessages = unreadCount > 0
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@class TSMessage;
|
||||
@class TSOutgoingMessage;
|
||||
@class TSQuotedMessage;
|
||||
@class YapDatabaseReadTransaction;
|
||||
|
||||
@protocol ConversationViewCellDelegate <NSObject>
|
||||
|
||||
|
@ -71,7 +72,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// The width of the collection view.
|
||||
@property (nonatomic) int contentWidth;
|
||||
|
||||
- (void)loadForDisplay;
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
- (CGSize)cellSizeForViewWidth:(int)viewWidth contentWidth:(int)contentWidth;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ConversationViewCell.h"
|
||||
|
@ -19,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.contentWidth = 0;
|
||||
}
|
||||
|
||||
- (void)loadForDisplay
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSFail(@"%@ This method should be overridden.", self.logTag);
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)loadForDisplay
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[OWSContactOffersInteraction class]]);
|
||||
|
|
|
@ -150,7 +150,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
#pragma mark - Load
|
||||
|
||||
- (void)loadForDisplay
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert(self.viewItem.interaction);
|
||||
|
|
|
@ -73,7 +73,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)loadForDisplay
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.viewItem);
|
||||
|
||||
|
@ -83,7 +83,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.imageView.image = [icon imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
self.imageView.tintColor = [self iconColorForInteraction:interaction];
|
||||
self.titleLabel.textColor = [self textColor];
|
||||
[self applyTitleForInteraction:interaction label:self.titleLabel];
|
||||
[self applyTitleForInteraction:interaction label:self.titleLabel transaction:transaction];
|
||||
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
@ -162,7 +162,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return result;
|
||||
}
|
||||
|
||||
- (void)applyTitleForInteraction:(TSInteraction *)interaction label:(UILabel *)label
|
||||
- (void)applyTitleForInteraction:(TSInteraction *)interaction
|
||||
label:(UILabel *)label
|
||||
transaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(interaction);
|
||||
OWSAssert(label);
|
||||
|
@ -170,22 +172,24 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// TODO: Should we move the copy generation into this view?
|
||||
|
||||
if ([interaction isKindOfClass:[TSErrorMessage class]]) {
|
||||
label.text = interaction.description;
|
||||
TSErrorMessage *errorMessage = (TSErrorMessage *)interaction;
|
||||
label.text = [errorMessage previewTextWithTransaction:transaction];
|
||||
} else if ([interaction isKindOfClass:[TSInfoMessage class]]) {
|
||||
if ([interaction isKindOfClass:[OWSVerificationStateChangeMessage class]]) {
|
||||
OWSVerificationStateChangeMessage *message = (OWSVerificationStateChangeMessage *)interaction;
|
||||
BOOL isVerified = message.verificationState == OWSVerificationStateVerified;
|
||||
TSInfoMessage *infoMessage = (TSInfoMessage *)interaction;
|
||||
if ([infoMessage isKindOfClass:[OWSVerificationStateChangeMessage class]]) {
|
||||
OWSVerificationStateChangeMessage *verificationMessage = (OWSVerificationStateChangeMessage *)infoMessage;
|
||||
BOOL isVerified = verificationMessage.verificationState == OWSVerificationStateVerified;
|
||||
NSString *displayName =
|
||||
[[Environment current].contactsManager displayNameForPhoneIdentifier:message.recipientId];
|
||||
[[Environment current].contactsManager displayNameForPhoneIdentifier:verificationMessage.recipientId];
|
||||
NSString *titleFormat = (isVerified
|
||||
? (message.isLocalChange
|
||||
? (verificationMessage.isLocalChange
|
||||
? NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_LOCAL",
|
||||
@"Format for info message indicating that the verification state was verified on "
|
||||
@"this device. Embeds {{user's name or phone number}}.")
|
||||
: NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_VERIFIED_OTHER_DEVICE",
|
||||
@"Format for info message indicating that the verification state was verified on "
|
||||
@"another device. Embeds {{user's name or phone number}}."))
|
||||
: (message.isLocalChange
|
||||
: (verificationMessage.isLocalChange
|
||||
? NSLocalizedString(@"VERIFICATION_STATE_CHANGE_FORMAT_NOT_VERIFIED_LOCAL",
|
||||
@"Format for info message indicating that the verification state was unverified on "
|
||||
@"this device. Embeds {{user's name or phone number}}.")
|
||||
|
@ -194,10 +198,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@"another device. Embeds {{user's name or phone number}}.")));
|
||||
label.text = [NSString stringWithFormat:titleFormat, displayName];
|
||||
} else {
|
||||
label.text = interaction.description;
|
||||
label.text = [infoMessage previewTextWithTransaction:transaction];
|
||||
}
|
||||
} else if ([interaction isKindOfClass:[TSCall class]]) {
|
||||
label.text = interaction.description;
|
||||
TSCall *call = (TSCall *)interaction;
|
||||
label.text = [call previewTextWithTransaction:transaction];
|
||||
} else {
|
||||
OWSFail(@"Unknown interaction type: %@", [interaction class]);
|
||||
label.text = nil;
|
||||
|
@ -266,7 +271,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
result.height += self.topVMargin;
|
||||
result.height += self.bottomVMargin;
|
||||
|
||||
[self applyTitleForInteraction:interaction label:self.titleLabel];
|
||||
// FIXME pass in transaction from the uiDBConnection.
|
||||
[[TSYapDatabaseObject dbReadConnection] readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[self applyTitleForInteraction:interaction label:self.titleLabel transaction:transaction];
|
||||
}];
|
||||
|
||||
CGFloat maxTitleWidth = (viewWidth - ([self hMargin] * 2.f + [self hSpacing] + [self iconSize]));
|
||||
CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(maxTitleWidth, CGFLOAT_MAX)];
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)loadForDisplay
|
||||
- (void)loadForDisplayWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
OWSAssert(self.viewItem);
|
||||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSUnreadIndicatorInteraction class]]);
|
||||
|
|
|
@ -4757,7 +4757,9 @@ typedef enum : NSUInteger {
|
|||
}
|
||||
cell.contentWidth = self.layout.contentWidth;
|
||||
|
||||
[cell loadForDisplay];
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
[cell loadForDisplayWithTransaction:transaction];
|
||||
}];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
|
|
@ -3223,7 +3223,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
TSContactThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:phoneNumber.toE164];
|
||||
[self sendFakeMessages:messageCount thread:contactThread];
|
||||
DDLogError(@"Create fake thread: %@, interactions: %zd",
|
||||
DDLogError(@"Create fake thread: %@, interactions: %tu",
|
||||
phoneNumber.toE164,
|
||||
contactThread.numberOfInteractions);
|
||||
}];
|
||||
|
@ -3251,7 +3251,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self sendFakeMessages:batchSize thread:thread isTextOnly:isTextOnly transaction:transaction];
|
||||
}];
|
||||
remainder -= batchSize;
|
||||
DDLogInfo(@"%@ sendFakeMessages %zd / %zd", self.logTag, counter - remainder, counter);
|
||||
DDLogInfo(@"%@ sendFakeMessages %td / %tu", self.logTag, counter - remainder, counter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3263,7 +3263,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
isTextOnly:(BOOL)isTextOnly
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
DDLogInfo(@"%@ sendFakeMessages: %zd", self.logTag, counter);
|
||||
DDLogInfo(@"%@ sendFakeMessages: %tu", self.logTag, counter);
|
||||
|
||||
for (NSUInteger i = 0; i < counter; i++) {
|
||||
NSString *randomText = [self randomText];
|
||||
|
@ -3411,7 +3411,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
{
|
||||
OWSAssert(thread);
|
||||
|
||||
DDLogInfo(@"%@ injectIncomingMessageInThread: %zd", self.logTag, counter);
|
||||
DDLogInfo(@"%@ injectIncomingMessageInThread: %tu", self.logTag, counter);
|
||||
|
||||
NSString *randomText = [self randomText];
|
||||
NSString *text = [[[@(counter) description] stringByAppendingString:@" "] stringByAppendingString:randomText];
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class OWSContactsManager;
|
||||
@class TSThread;
|
||||
@class ThreadViewModel;
|
||||
@class YapDatabaseReadTransaction;
|
||||
|
||||
@interface HomeViewCell : UITableViewCell
|
||||
|
||||
|
@ -13,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (NSString *)cellReuseIdentifier;
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread
|
||||
- (void)configureWithThread:(ThreadViewModel *)thread
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet;
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#import <SignalServiceKit/OWSMessageManager.h>
|
||||
#import <SignalServiceKit/TSContactThread.h>
|
||||
#import <SignalServiceKit/TSGroupThread.h>
|
||||
#import <SignalServiceKit/TSThread.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
@ -27,7 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic) UIView *unreadBadge;
|
||||
@property (nonatomic) UILabel *unreadLabel;
|
||||
|
||||
@property (nonatomic, nullable) TSThread *thread;
|
||||
@property (nonatomic, nullable) ThreadViewModel *thread;
|
||||
@property (nonatomic, nullable) OWSContactsManager *contactsManager;
|
||||
|
||||
@property (nonatomic, readonly) NSMutableArray<NSLayoutConstraint *> *viewConstraints;
|
||||
|
@ -143,9 +142,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return NSStringFromClass(self.class);
|
||||
}
|
||||
|
||||
- (void)configureWithThread:(TSThread *)thread
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
|
||||
- (void)configureWithThread:(ThreadViewModel *)thread
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
OWSAssert(thread);
|
||||
|
@ -183,7 +182,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.dateTimeLabel.font = self.unreadFont;
|
||||
}
|
||||
|
||||
NSUInteger unreadCount = [[OWSMessageUtils sharedManager] unreadMessagesInThread:thread];
|
||||
NSUInteger unreadCount = thread.unreadCount;
|
||||
if (unreadCount == 0) {
|
||||
[self.viewConstraints addObject:[self.payloadView autoPinTrailingToSuperviewMargin]];
|
||||
} else {
|
||||
|
@ -240,18 +239,19 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return;
|
||||
}
|
||||
|
||||
TSThread *thread = self.thread;
|
||||
ThreadViewModel *thread = self.thread;
|
||||
if (thread == nil) {
|
||||
OWSFail(@"%@ thread should not be nil", self.logTag);
|
||||
self.avatarView.image = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
self.avatarView.image =
|
||||
[OWSAvatarBuilder buildImageForThread:thread diameter:self.avatarSize contactsManager:contactsManager];
|
||||
self.avatarView.image = [OWSAvatarBuilder buildImageForThread:thread.threadRecord
|
||||
diameter:self.avatarSize
|
||||
contactsManager:contactsManager];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedSnippetForThread:(TSThread *)thread
|
||||
- (NSAttributedString *)attributedSnippetForThread:(ThreadViewModel *)thread
|
||||
blockedPhoneNumberSet:(NSSet<NSString *> *)blockedPhoneNumberSet
|
||||
{
|
||||
OWSAssert(thread);
|
||||
|
@ -285,7 +285,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
: [UIColor lightGrayColor]),
|
||||
}]];
|
||||
}
|
||||
NSString *displayableText = thread.lastMessageLabel.filterStringForDisplay;
|
||||
NSString *displayableText = thread.lastMessageText;
|
||||
if (displayableText) {
|
||||
[snippetText appendAttributedString:[[NSAttributedString alloc]
|
||||
initWithString:displayableText
|
||||
|
@ -447,7 +447,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
self.nameLabel.font = self.nameFont;
|
||||
|
||||
TSThread *thread = self.thread;
|
||||
ThreadViewModel *thread = self.thread;
|
||||
if (thread == nil) {
|
||||
OWSFail(@"%@ thread should not be nil", self.logTag);
|
||||
self.nameLabel.attributedText = nil;
|
||||
|
|
|
@ -47,7 +47,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
@property (nonatomic) UISegmentedControl *segmentedControl;
|
||||
@property (nonatomic) id previewingContext;
|
||||
@property (nonatomic) NSSet<NSString *> *blockedPhoneNumberSet;
|
||||
|
||||
@property (nonatomic, readonly) NSCache<NSString *, ThreadViewModel *> *threadViewModelCache;
|
||||
@property (nonatomic) BOOL isViewVisible;
|
||||
@property (nonatomic) BOOL isAppInBackground;
|
||||
@property (nonatomic) BOOL shouldObserveDBModifications;
|
||||
|
@ -108,6 +108,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
_messageSender = [Environment current].messageSender;
|
||||
_blockingManager = [OWSBlockingManager sharedManager];
|
||||
_blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]];
|
||||
_threadViewModelCache = [NSCache new];
|
||||
|
||||
// Ensure ExperienceUpgradeFinder has been initialized.
|
||||
[ExperienceUpgradeFinder sharedManager];
|
||||
|
@ -155,14 +156,14 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
|
||||
_blockedPhoneNumberSet = [NSSet setWithArray:[_blockingManager blockedPhoneNumbers]];
|
||||
|
||||
[self.tableView reloadData];
|
||||
[self reloadTableViewData];
|
||||
}
|
||||
|
||||
- (void)signalAccountsDidChange:(id)notification
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
[self.tableView reloadData];
|
||||
[self reloadTableViewData];
|
||||
}
|
||||
|
||||
#pragma mark - View Life Cycle
|
||||
|
@ -395,7 +396,11 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
if ([TSThread numberOfKeysInCollection] > 0) {
|
||||
__block BOOL hasAnyMessages;
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
|
||||
hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction];
|
||||
}];
|
||||
if (hasAnyMessages) {
|
||||
[self.contactsManager requestSystemContactsOnceWithCompletion:^(NSError *_Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateReminderViews];
|
||||
|
@ -468,6 +473,13 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
}
|
||||
}
|
||||
|
||||
- (void)reloadTableViewData
|
||||
{
|
||||
// PERF: come up with a more nuanced cache clearing scheme
|
||||
[self.threadViewModelCache removeAllObjects];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
- (void)resetMappings
|
||||
{
|
||||
// If we're entering "active" mode (e.g. view is visible and app is in foreground),
|
||||
|
@ -484,13 +496,18 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
}];
|
||||
}
|
||||
|
||||
[[self tableView] reloadData];
|
||||
[self reloadTableViewData];
|
||||
|
||||
[self checkIfEmptyView];
|
||||
[self updateInboxCountLabel];
|
||||
|
||||
// If the user hasn't already granted contact access
|
||||
// we don't want to request until they receive a message.
|
||||
if ([TSThread numberOfKeysInCollection] > 0) {
|
||||
__block BOOL hasAnyMessages;
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
|
||||
hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction];
|
||||
}];
|
||||
if (hasAnyMessages) {
|
||||
[self.contactsManager requestSystemContactsOnce];
|
||||
}
|
||||
}
|
||||
|
@ -506,11 +523,21 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
self.isAppInBackground = YES;
|
||||
}
|
||||
|
||||
- (BOOL)hasAnyMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [TSThread numberOfKeysInCollectionWithTransaction:transaction] > 0;
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(NSNotification *)notification
|
||||
{
|
||||
// It's possible a thread was created while we where in the background. But since we don't honor contact
|
||||
// requests unless the app is in the foregrond, we must check again here upon becoming active.
|
||||
if ([TSThread numberOfKeysInCollection] > 0) {
|
||||
__block BOOL hasAnyMessages;
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction * _Nonnull transaction) {
|
||||
hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction];
|
||||
}];
|
||||
|
||||
if (hasAnyMessages) {
|
||||
[self.contactsManager requestSystemContactsOnceWithCompletion:^(NSError *_Nullable error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateReminderViews];
|
||||
|
@ -577,13 +604,29 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
return (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section];
|
||||
}
|
||||
|
||||
- (ThreadViewModel *)threadViewModelForIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
TSThread *threadRecord = [self threadForIndexPath:indexPath];
|
||||
|
||||
ThreadViewModel *_Nullable cachedThreadViewModel = [self.threadViewModelCache objectForKey:threadRecord.uniqueId];
|
||||
if (cachedThreadViewModel) {
|
||||
return cachedThreadViewModel;
|
||||
}
|
||||
|
||||
__block ThreadViewModel *_Nullable newThreadViewModel;
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
newThreadViewModel = [[ThreadViewModel alloc] initWithThread:threadRecord transaction:transaction];
|
||||
}];
|
||||
[self.threadViewModelCache setObject:newThreadViewModel forKey:threadRecord.uniqueId];
|
||||
return newThreadViewModel;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
HomeViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:HomeViewCell.cellReuseIdentifier];
|
||||
OWSAssert(cell);
|
||||
|
||||
TSThread *thread = [self threadForIndexPath:indexPath];
|
||||
|
||||
ThreadViewModel *thread = [self threadViewModelForIndexPath:indexPath];
|
||||
[cell configureWithThread:thread
|
||||
contactsManager:self.contactsManager
|
||||
blockedPhoneNumberSet:self.blockedPhoneNumberSet];
|
||||
|
@ -891,7 +934,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
|
||||
[self resetMappings];
|
||||
|
||||
[[self tableView] reloadData];
|
||||
[self reloadTableViewData];
|
||||
[self checkIfEmptyView];
|
||||
[self updateReminderViews];
|
||||
}
|
||||
|
@ -904,6 +947,8 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
|
||||
if (!_uiDatabaseConnection) {
|
||||
_uiDatabaseConnection = [OWSPrimaryStorage.sharedManager newDatabaseConnection];
|
||||
// default is 250
|
||||
_uiDatabaseConnection.objectCacheLimit = 500;
|
||||
[_uiDatabaseConnection beginLongLivedReadTransaction];
|
||||
}
|
||||
return _uiDatabaseConnection;
|
||||
|
@ -948,7 +993,12 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
|
||||
// If the user hasn't already granted contact access
|
||||
// we don't want to request until they receive a message.
|
||||
if ([TSThread numberOfKeysInCollection] > 0) {
|
||||
__block BOOL hasAnyMessages;
|
||||
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
|
||||
hasAnyMessages = [self hasAnyMessagesWithTransaction:transaction];
|
||||
}];
|
||||
|
||||
if (hasAnyMessages) {
|
||||
[self.contactsManager requestSystemContactsOnce];
|
||||
}
|
||||
|
||||
|
@ -989,6 +1039,10 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
|
|||
}
|
||||
|
||||
for (YapDatabaseViewRowChange *rowChange in rowChanges) {
|
||||
NSString *key = rowChange.collectionKey.key;
|
||||
OWSAssert(key);
|
||||
[self.threadViewModelCache removeObjectForKey:key];
|
||||
|
||||
switch (rowChange.type) {
|
||||
case YapDatabaseViewChangeDelete: {
|
||||
[self.tableView deleteRowsAtIndexPaths:@[ rowChange.indexPath ]
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#import <SignalServiceKit/TSIncomingMessage.h>
|
||||
#import <SignalServiceKit/TextSecureKitEnv.h>
|
||||
#import <SignalServiceKit/Threading.h>
|
||||
#import <YapDatabase/YapDatabaseTransaction.h>
|
||||
|
||||
@interface NotificationsManager ()
|
||||
|
||||
|
@ -200,51 +201,56 @@
|
|||
|
||||
#pragma mark - Signal Messages
|
||||
|
||||
- (void)notifyUserForErrorMessage:(TSErrorMessage *)message inThread:(TSThread *)thread {
|
||||
- (void)notifyUserForErrorMessage:(TSErrorMessage *)message
|
||||
thread:(TSThread *)thread
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssert(message);
|
||||
OWSAssert(thread);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (thread.isMuted) {
|
||||
return;
|
||||
}
|
||||
NSString *messageText = [message previewTextWithTransaction:transaction];
|
||||
|
||||
BOOL shouldPlaySound = [self shouldPlaySoundForNotification];
|
||||
[transaction
|
||||
addCompletionQueue:nil
|
||||
completionBlock:^() {
|
||||
if (thread.isMuted) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *messageDescription = message.description;
|
||||
BOOL shouldPlaySound = [self shouldPlaySoundForNotification];
|
||||
|
||||
if (([UIApplication sharedApplication].applicationState != UIApplicationStateActive) && messageDescription) {
|
||||
UILocalNotification *notification = [[UILocalNotification alloc] init];
|
||||
notification.userInfo = @{ Signal_Thread_UserInfo_Key : thread.uniqueId };
|
||||
if (shouldPlaySound) {
|
||||
OWSSound sound = [OWSSounds notificationSoundForThread:thread];
|
||||
notification.soundName = [OWSSounds filenameForSound:sound];
|
||||
}
|
||||
if (([UIApplication sharedApplication].applicationState != UIApplicationStateActive) && messageText) {
|
||||
UILocalNotification *notification = [[UILocalNotification alloc] init];
|
||||
notification.userInfo = @{ Signal_Thread_UserInfo_Key : thread.uniqueId };
|
||||
if (shouldPlaySound) {
|
||||
OWSSound sound = [OWSSounds notificationSoundForThread:thread];
|
||||
notification.soundName = [OWSSounds filenameForSound:sound];
|
||||
}
|
||||
|
||||
NSString *alertBodyString = @"";
|
||||
NSString *alertBodyString = @"";
|
||||
|
||||
NSString *authorName = [thread name];
|
||||
switch (self.notificationPreviewType) {
|
||||
case NotificationNamePreview:
|
||||
case NotificationNameNoPreview:
|
||||
alertBodyString = [NSString stringWithFormat:@"%@: %@", authorName, messageDescription];
|
||||
break;
|
||||
case NotificationNoNameNoPreview:
|
||||
alertBodyString = messageDescription;
|
||||
break;
|
||||
}
|
||||
notification.alertBody = alertBodyString;
|
||||
NSString *authorName = [thread name];
|
||||
switch (self.notificationPreviewType) {
|
||||
case NotificationNamePreview:
|
||||
case NotificationNameNoPreview:
|
||||
alertBodyString = [NSString stringWithFormat:@"%@: %@", authorName, messageText];
|
||||
break;
|
||||
case NotificationNoNameNoPreview:
|
||||
alertBodyString = messageText;
|
||||
break;
|
||||
}
|
||||
notification.alertBody = alertBodyString;
|
||||
|
||||
[[PushManager sharedManager] presentNotification:notification checkForCancel:NO];
|
||||
} else {
|
||||
if (shouldPlaySound && [Environment.preferences soundInForeground]) {
|
||||
OWSSound sound = [OWSSounds notificationSoundForThread:thread];
|
||||
SystemSoundID soundId = [OWSSounds systemSoundIDForSound:sound quiet:YES];
|
||||
// Vibrate, respect silent switch, respect "Alert" volume, not media volume.
|
||||
AudioServicesPlayAlertSound(soundId);
|
||||
}
|
||||
}
|
||||
});
|
||||
[[PushManager sharedManager] presentNotification:notification checkForCancel:NO];
|
||||
} else {
|
||||
if (shouldPlaySound && [Environment.preferences soundInForeground]) {
|
||||
OWSSound sound = [OWSSounds notificationSoundForThread:thread];
|
||||
SystemSoundID soundId = [OWSSounds systemSoundIDForSound:sound quiet:YES];
|
||||
// Vibrate, respect silent switch, respect "Alert" volume, not media volume.
|
||||
AudioServicesPlayAlertSound(soundId);
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)notifyUserForIncomingMessage:(TSIncomingMessage *)message
|
||||
|
|
|
@ -371,9 +371,15 @@ NSString *const OWSContactsManagerSignalAccountsDidChangeNotification
|
|||
{
|
||||
OWSAssert(recipientId.length > 0);
|
||||
|
||||
SignalAccount *signalAccount = [self signalAccountForRecipientId:recipientId];
|
||||
SignalAccount *_Nullable signalAccount = [self signalAccountForRecipientId:recipientId];
|
||||
if (!signalAccount) {
|
||||
return nil;
|
||||
// search system contacts for no-longer-registered signal users, for which there will be no SignalAccount
|
||||
DDLogDebug(@"%@ no signal account", self.logTag);
|
||||
Contact *_Nullable nonSignalContact = self.allContactsMap[recipientId];
|
||||
if (!nonSignalContact) {
|
||||
return nil;
|
||||
}
|
||||
return nonSignalContact.fullName;
|
||||
}
|
||||
|
||||
NSString *fullName = signalAccount.contactFullName;
|
||||
|
|
|
@ -7,11 +7,11 @@ import SignalServiceKit
|
|||
@objc
|
||||
public class NoopNotificationsManager: NSObject, NotificationsProtocol {
|
||||
|
||||
public func notifyUser(for incomingMessage: TSIncomingMessage!, in thread: TSThread!, contactsManager: ContactsManagerProtocol!, transaction: YapDatabaseReadTransaction!) {
|
||||
public func notifyUser(for incomingMessage: TSIncomingMessage, in thread: TSThread, contactsManager: ContactsManagerProtocol, transaction: YapDatabaseReadTransaction) {
|
||||
owsFail("\(self.logTag) in \(#function).")
|
||||
}
|
||||
|
||||
public func notifyUser(for error: TSErrorMessage!, in thread: TSThread!) {
|
||||
public func notifyUser(for error: TSErrorMessage, thread: TSThread, transaction: YapDatabaseReadWriteTransaction) {
|
||||
Logger.warn("\(self.logTag) in \(#function), skipping notification for: \(error.description)")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,12 +65,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (NSArray<TSInvalidIdentityKeyReceivingErrorMessage *> *)receivedMessagesForInvalidKey:(NSData *)key;
|
||||
|
||||
/**
|
||||
* Returns whether or not the thread has unread messages.
|
||||
*
|
||||
* @return YES if it has unread TSIncomingMessages, NO otherwise.
|
||||
*/
|
||||
- (BOOL)hasUnreadMessages;
|
||||
- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
NS_SWIFT_NAME(unreadMessageCount(transaction:));
|
||||
|
||||
- (BOOL)hasSafetyNumbers;
|
||||
|
||||
|
@ -90,7 +86,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*
|
||||
* @return Thread preview string.
|
||||
*/
|
||||
- (NSString *)lastMessageLabel;
|
||||
- (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
NS_SWIFT_NAME(lastMessageText(transaction:));
|
||||
|
||||
/**
|
||||
* Updates the thread's caches of the latest interaction.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#import "TSThread.h"
|
||||
#import "NSDate+OWS.h"
|
||||
#import "NSString+SSK.h"
|
||||
#import "OWSPrimaryStorage.h"
|
||||
#import "OWSReadTracking.h"
|
||||
#import "TSDatabaseView.h"
|
||||
|
@ -24,7 +25,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic, copy) NSString *messageDraft;
|
||||
@property (atomic, nullable) NSDate *mutedUntilDate;
|
||||
|
||||
- (TSInteraction *)lastInteraction;
|
||||
- (TSInteraction *)lastInteractionWithTranscation:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -199,17 +200,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return count;
|
||||
}
|
||||
|
||||
- (BOOL)hasUnreadMessages {
|
||||
TSInteraction *interaction = self.lastInteraction;
|
||||
BOOL hasUnread = NO;
|
||||
|
||||
if ([interaction isKindOfClass:[TSIncomingMessage class]]) {
|
||||
hasUnread = ![(TSIncomingMessage *)interaction wasRead];
|
||||
}
|
||||
|
||||
return hasUnread;
|
||||
}
|
||||
|
||||
- (NSArray<id<OWSReadTracking>> *)unseenMessagesWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
NSMutableArray<id<OWSReadTracking>> *messages = [NSMutableArray new];
|
||||
|
@ -228,6 +218,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return [messages copy];
|
||||
}
|
||||
|
||||
- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
return [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:self.uniqueId];
|
||||
}
|
||||
|
||||
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {
|
||||
|
@ -238,34 +233,34 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSAssert([self unseenMessagesWithTransaction:transaction].count < 1);
|
||||
}
|
||||
|
||||
- (TSInteraction *) lastInteraction {
|
||||
__block TSInteraction *last;
|
||||
[OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
last = [[transaction ext:TSMessageDatabaseViewExtensionName] lastObjectInGroup:self.uniqueId];
|
||||
}];
|
||||
return last;
|
||||
}
|
||||
|
||||
- (TSInteraction *)lastInteractionForInbox
|
||||
- (TSInteraction *)lastInteractionForInboxWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
__block NSUInteger missedCount = 0;
|
||||
__block TSInteraction *last = nil;
|
||||
[OWSPrimaryStorage.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
[[transaction ext:TSMessageDatabaseViewExtensionName]
|
||||
enumerateRowsInGroup:self.uniqueId
|
||||
withOptions:NSEnumerationReverse
|
||||
usingBlock:^(
|
||||
NSString *collection, NSString *key, id object, id metadata, NSUInteger index, BOOL *stop) {
|
||||
[[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]]);
|
||||
|
||||
OWSAssert([object isKindOfClass:[TSInteraction class]]);
|
||||
missedCount++;
|
||||
TSInteraction *interaction = (TSInteraction *)object;
|
||||
|
||||
if ([TSThread shouldInteractionAppearInInbox:interaction]) {
|
||||
last = interaction;
|
||||
|
||||
TSInteraction *interaction = (TSInteraction *)object;
|
||||
|
||||
if ([TSThread shouldInteractionAppearInInbox:interaction]) {
|
||||
last = interaction;
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
}];
|
||||
// For long ignored threads, with lots of SN changes this can get really slow.
|
||||
// I see this in development because I have a lot of long forgotten threads with members
|
||||
// who's test devices are constantly reinstalled. We could add a purpose-built DB view,
|
||||
// but I think in the real world this is rare to be a hotspot.
|
||||
if (missedCount > 50) {
|
||||
DDLogWarn(@"%@ found last interaction for inbox after skipping %tu items", self.logTag, missedCount);
|
||||
}
|
||||
*stop = YES;
|
||||
}
|
||||
}];
|
||||
return last;
|
||||
}
|
||||
|
||||
|
@ -277,12 +272,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
- (NSString *)lastMessageLabel {
|
||||
TSInteraction *interaction = self.lastInteractionForInbox;
|
||||
if (interaction == nil) {
|
||||
return @"";
|
||||
- (NSString *)lastMessageTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
TSInteraction *interaction = [self lastInteractionForInboxWithTransaction:transaction];
|
||||
if ([interaction conformsToProtocol:@protocol(OWSPreviewText)]) {
|
||||
id<OWSPreviewText> previewable = (id<OWSPreviewText>)interaction;
|
||||
return [previewable previewTextWithTransaction:transaction].filterStringForDisplay;
|
||||
} else {
|
||||
return interaction.description;
|
||||
return @"";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return !![[OWSIdentityManager sharedManager] identityKeyForRecipientId:self.contactIdentifier];
|
||||
}
|
||||
|
||||
// TODO deprecate this? seems weird to access the displayName in the DB model
|
||||
- (NSString *)name
|
||||
{
|
||||
return [[TextSecureKitEnv sharedEnv].contactsManager displayNameForPhoneIdentifier:self.contactIdentifier];
|
||||
|
|
|
@ -56,7 +56,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
-(NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
if (self.createdByRemoteName) {
|
||||
if (self.configurationIsEnabled && self.configurationDurationSeconds > 0) {
|
||||
|
|
|
@ -97,7 +97,8 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
|
|||
return OWSInteractionType_Error;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
switch (_errorType) {
|
||||
case TSErrorMessageNoSession:
|
||||
return NSLocalizedString(@"ERROR_MESSAGE_NO_SESSION", @"");
|
||||
|
|
|
@ -91,7 +91,8 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
|
|||
return OWSInteractionType_Info;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
switch (_messageType) {
|
||||
case TSInfoMessageTypeSessionDidEnd:
|
||||
return NSLocalizedString(@"SECURE_SESSION_RESET", nil);
|
||||
|
|
|
@ -19,6 +19,12 @@ typedef NS_ENUM(NSInteger, OWSInteractionType) {
|
|||
OWSInteractionType_Offer,
|
||||
};
|
||||
|
||||
@protocol OWSPreviewText
|
||||
|
||||
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
@interface TSInteraction : TSYapDatabaseObject
|
||||
|
||||
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread;
|
||||
|
|
|
@ -127,8 +127,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return OWSInteractionType_Unknown;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
return @"Interaction description";
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString
|
||||
stringWithFormat:@"%@ in thread: %@ timestamp: %tu", [super description], self.uniqueThreadId, self.timestamp];
|
||||
}
|
||||
|
||||
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction {
|
||||
|
|
|
@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@class TSQuotedMessage;
|
||||
@class YapDatabaseReadWriteTransaction;
|
||||
|
||||
@interface TSMessage : TSInteraction
|
||||
@interface TSMessage : TSInteraction <OWSPreviewText>
|
||||
|
||||
@property (nonatomic, readonly) NSMutableArray<NSString *> *attachmentIds;
|
||||
@property (nonatomic, readonly, nullable) NSString *body;
|
||||
|
@ -39,7 +39,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (BOOL)hasAttachments;
|
||||
- (nullable TSAttachment *)attachmentWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction;
|
||||
- (void)setQuotedMessageThumbnailAttachmentStream:(TSAttachmentStream *)attachmentStream;
|
||||
|
||||
- (BOOL)shouldStartExpireTimer;
|
||||
|
|
|
@ -265,16 +265,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
|
|||
}
|
||||
}
|
||||
|
||||
// TODO deprecate this and implement something like previewTextWithTransaction: for all TSInteractions
|
||||
- (NSString *)description
|
||||
{
|
||||
__block NSString *result;
|
||||
[self.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
result = [self previewTextWithTransaction:transaction];
|
||||
}];
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[super removeWithTransaction:transaction];
|
||||
|
|
|
@ -437,7 +437,7 @@ NSString *const kTSOutgoingMessageSentRecipientAll = @"kTSOutgoingMessageSentRec
|
|||
[builder setBody:self.body];
|
||||
} else {
|
||||
OWSFail(@"%@ message body length too long.", self.logTag);
|
||||
NSMutableString *truncatedBody = [self.body mutableCopy];
|
||||
NSString *truncatedBody = self.body;
|
||||
while ([truncatedBody lengthOfBytesUsingEncoding:NSUTF8StringEncoding] > kOversizeTextMessageSizeThreshold) {
|
||||
DDLogError(@"%@ truncating body which is too long: %tu",
|
||||
self.logTag,
|
||||
|
|
|
@ -545,7 +545,9 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa
|
|||
[message saveWithTransaction:transaction];
|
||||
}
|
||||
|
||||
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread];
|
||||
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage
|
||||
thread:contactThread
|
||||
transaction:transaction];
|
||||
}
|
||||
|
||||
- (void)enqueueSyncMessageForVerificationStateForRecipientId:(NSString *)recipientId
|
||||
|
|
|
@ -269,8 +269,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
exception.name,
|
||||
exception.reason);
|
||||
|
||||
__block TSErrorMessage *errorMessage;
|
||||
|
||||
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
TSErrorMessage *errorMessage;
|
||||
|
||||
if ([exception.name isEqualToString:NoSessionException]) {
|
||||
OWSProdErrorWEnvelope([OWSAnalyticsEvents messageManagerErrorNoSession], envelope);
|
||||
errorMessage = [TSErrorMessage missingSessionWithEnvelope:envelope withTransaction:transaction];
|
||||
|
@ -298,18 +300,21 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
OWSAssert(errorMessage);
|
||||
[errorMessage saveWithTransaction:transaction];
|
||||
if (errorMessage != nil) {
|
||||
[errorMessage saveWithTransaction:transaction];
|
||||
[self notifyUserForErrorMessage:errorMessage envelope:envelope transaction:transaction];
|
||||
}
|
||||
}];
|
||||
|
||||
if (errorMessage != nil) {
|
||||
[self notifyForErrorMessage:errorMessage withEnvelope:envelope];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)notifyForErrorMessage:(TSErrorMessage *)errorMessage withEnvelope:(OWSSignalServiceProtosEnvelope *)envelope
|
||||
- (void)notifyUserForErrorMessage:(TSErrorMessage *)errorMessage
|
||||
envelope:(OWSSignalServiceProtosEnvelope *)envelope
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source];
|
||||
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage inThread:contactThread];
|
||||
TSThread *contactThread = [TSContactThread getOrCreateThreadWithContactId:envelope.source transaction:transaction];
|
||||
[[TextSecureKitEnv sharedEnv].notificationsManager notifyUserForErrorMessage:errorMessage
|
||||
thread:contactThread
|
||||
transaction:transaction];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (NSUInteger)unreadMessagesCount;
|
||||
- (NSUInteger)unreadMessagesCountExcept:(TSThread *)thread;
|
||||
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread;
|
||||
|
||||
- (void)updateApplicationBadgeCount;
|
||||
|
||||
|
|
|
@ -95,14 +95,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[CurrentAppContext() setMainAppBadgeNumber:numberOfItems];
|
||||
}
|
||||
|
||||
- (NSUInteger)unreadMessagesInThread:(TSThread *)thread
|
||||
{
|
||||
__block NSUInteger numberOfItems;
|
||||
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
|
||||
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:thread.uniqueId];
|
||||
}];
|
||||
return numberOfItems;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ typedef enum {
|
|||
RPRecentCallTypeIncomingDeclined
|
||||
} RPRecentCallType;
|
||||
|
||||
@interface TSCall : TSInteraction <OWSReadTracking>
|
||||
@interface TSCall : TSInteraction <OWSReadTracking, OWSPreviewText>
|
||||
|
||||
@property (nonatomic, readonly) RPRecentCallType callType;
|
||||
|
||||
|
|
|
@ -67,7 +67,9 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
|
|||
return OWSInteractionType_Call;
|
||||
}
|
||||
|
||||
- (NSString *)description {
|
||||
- (NSString *)previewTextWithTransaction:(YapDatabaseReadTransaction *)transaction
|
||||
{
|
||||
// We don't actually use the `transaction` but other sibling classes do.
|
||||
switch (_callType) {
|
||||
case RPRecentCallTypeIncoming:
|
||||
return NSLocalizedString(@"INCOMING_CALL", @"");
|
||||
|
@ -83,7 +85,7 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
|
|||
return NSLocalizedString(@"INFO_MESSAGE_MISSED_CALL_DUE_TO_CHANGED_IDENITY", @"info message text shown in conversation view");
|
||||
case RPRecentCallTypeIncomingDeclined:
|
||||
return NSLocalizedString(@"INCOMING_DECLINED_CALL",
|
||||
@"info message recorded in conversation history when local user declined a call");
|
||||
@"info message recorded in conversation history when local user declined a call");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class TSErrorMessage;
|
||||
@class TSIncomingMessage;
|
||||
@class TSThread;
|
||||
@class YapDatabaseReadTransaction;
|
||||
|
||||
@protocol ContactsManagerProtocol;
|
||||
|
||||
@protocol NotificationsProtocol <NSObject>
|
||||
|
@ -15,6 +18,10 @@
|
|||
contactsManager:(id<ContactsManagerProtocol>)contactsManager
|
||||
transaction:(YapDatabaseReadTransaction *)transaction;
|
||||
|
||||
- (void)notifyUserForErrorMessage:(TSErrorMessage *)error inThread:(TSThread *)thread;
|
||||
- (void)notifyUserForErrorMessage:(TSErrorMessage *)error
|
||||
thread:(TSThread *)thread
|
||||
transaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -109,17 +109,20 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (YapDatabaseConnection *)dbReadConnection
|
||||
{
|
||||
OWSJanksUI();
|
||||
|
||||
// We use TSYapDatabaseObject's dbReadWriteConnection (not OWSPrimaryStorage's
|
||||
// dbReadConnection) for consistency, since we tend to [TSYapDatabaseObject
|
||||
// save] and want to write to the same connection we read from. To get true
|
||||
// consistency, we'd want to update entities by reading & writing from within
|
||||
// the same transaction, but that'll be a big refactor.
|
||||
|
||||
return self.dbReadWriteConnection;
|
||||
}
|
||||
|
||||
+ (YapDatabaseConnection *)dbReadWriteConnection
|
||||
{
|
||||
OWSJanksUI();
|
||||
|
||||
// Use a dedicated connection for model reads & writes.
|
||||
static YapDatabaseConnection *dbReadWriteConnection = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
|
|
|
@ -150,4 +150,26 @@ void SwiftAssertIsOnMainThread(NSString *functionName);
|
|||
userInfo:userInfoParam]; \
|
||||
}
|
||||
|
||||
|
||||
// UI JANK
|
||||
//
|
||||
// In pursuit of smooth UI, we want to continue moving blocking operations off the main thread.
|
||||
// Add `OWSJanksUI` in code paths that shouldn't be called on the main thread.
|
||||
// Because we have pervasively broken this tenant, enabling it by default would be too disruptive
|
||||
// but it's helpful while unjanking and maybe someday we can have it enabled by default.
|
||||
//#define DEBUG_UI_JANK 1
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_UI_JANK
|
||||
#define OWSJanksUI() \
|
||||
do { \
|
||||
OWSAssert(![NSThread isMainThread]) \
|
||||
} while (NO)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef OWSJanksUI
|
||||
#define OWSJanksUI()
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
Loading…
Reference in a new issue