Merge branch 'dev' into snode-list-updating

pull/394/head
nielsandriesse 4 years ago
commit accd838017

@ -5241,7 +5241,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 214; CURRENT_PROJECT_VERSION = 217;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = SUQ8J2PCT7; DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@ -5262,7 +5262,7 @@
INFOPLIST_FILE = SessionShareExtension/Meta/Info.plist; INFOPLIST_FILE = SessionShareExtension/Meta/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 1.9.5; MARKETING_VERSION = 1.9.8;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -5310,7 +5310,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 214; CURRENT_PROJECT_VERSION = 217;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = SUQ8J2PCT7; DEVELOPMENT_TEAM = SUQ8J2PCT7;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@ -5336,7 +5336,7 @@
INFOPLIST_FILE = SessionShareExtension/Meta/Info.plist; INFOPLIST_FILE = SessionShareExtension/Meta/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 1.9.5; MARKETING_VERSION = 1.9.8;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -5371,7 +5371,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 214; CURRENT_PROJECT_VERSION = 217;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = SUQ8J2PCT7; DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@ -5390,7 +5390,7 @@
INFOPLIST_FILE = SessionNotificationServiceExtension/Meta/Info.plist; INFOPLIST_FILE = SessionNotificationServiceExtension/Meta/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 1.9.5; MARKETING_VERSION = 1.9.8;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension";
@ -5441,7 +5441,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 214; CURRENT_PROJECT_VERSION = 217;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = SUQ8J2PCT7; DEVELOPMENT_TEAM = SUQ8J2PCT7;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
@ -5465,7 +5465,7 @@
INFOPLIST_FILE = SessionNotificationServiceExtension/Meta/Info.plist; INFOPLIST_FILE = SessionNotificationServiceExtension/Meta/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MARKETING_VERSION = 1.9.5; MARKETING_VERSION = 1.9.8;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension";
@ -6326,7 +6326,7 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 214; CURRENT_PROJECT_VERSION = 217;
DEVELOPMENT_TEAM = SUQ8J2PCT7; DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -6362,7 +6362,7 @@
"$(SRCROOT)", "$(SRCROOT)",
); );
LLVM_LTO = NO; LLVM_LTO = NO;
MARKETING_VERSION = 1.9.5; MARKETING_VERSION = 1.9.8;
OTHER_LDFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
@ -6394,7 +6394,7 @@
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 214; CURRENT_PROJECT_VERSION = 217;
DEVELOPMENT_TEAM = SUQ8J2PCT7; DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -6430,7 +6430,7 @@
"$(SRCROOT)", "$(SRCROOT)",
); );
LLVM_LTO = NO; LLVM_LTO = NO;
MARKETING_VERSION = 1.9.5; MARKETING_VERSION = 1.9.8;
OTHER_LDFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)";
PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger";
PRODUCT_NAME = Session; PRODUCT_NAME = Session;

@ -149,7 +149,8 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
Storage.read { transaction in Storage.read { transaction in
unreadCount = self.thread.unreadMessageCount(transaction: transaction) unreadCount = self.thread.unreadMessageCount(transaction: transaction)
} }
unreadViewItems = unreadCount != 0 ? [ConversationViewItem](viewItems[viewItems.endIndex - Int(unreadCount) ..< viewItems.endIndex]) : [] let clampedUnreadCount = min(unreadCount, UInt(kConversationInitialMaxRangeSize))
unreadViewItems = clampedUnreadCount != 0 ? [ConversationViewItem](viewItems[viewItems.endIndex - Int(clampedUnreadCount) ..< viewItems.endIndex]) : []
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@ -180,7 +181,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
addOrRemoveBlockedBanner() addOrRemoveBlockedBanner()
// Notifications // Notifications
let notificationCenter = NotificationCenter.default let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillShowNotification(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil) notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(handleAudioDidFinishPlayingNotification(_:)), name: .SNAudioDidFinishPlaying, object: nil) notificationCenter.addObserver(self, selector: #selector(handleAudioDidFinishPlayingNotification(_:)), name: .SNAudioDidFinishPlaying, object: nil)
notificationCenter.addObserver(self, selector: #selector(addOrRemoveBlockedBanner), name: NSNotification.Name(rawValue: kNSNotificationName_BlockListDidChange), object: nil) notificationCenter.addObserver(self, selector: #selector(addOrRemoveBlockedBanner), name: NSNotification.Name(rawValue: kNSNotificationName_BlockListDidChange), object: nil)
@ -288,7 +289,7 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
} }
} }
@objc func handleKeyboardWillShowNotification(_ notification: Notification) { @objc func handleKeyboardWillChangeFrameNotification(_ notification: Notification) {
guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return } guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return }
if (newHeight > 0 && baselineKeyboardHeight == 0) { if (newHeight > 0 && baselineKeyboardHeight == 0) {
baselineKeyboardHeight = newHeight baselineKeyboardHeight = newHeight
@ -299,8 +300,8 @@ final class ConversationVC : BaseVC, ConversationViewModelDelegate, OWSConversat
scrollButton.pin(.bottom, to: .bottom, of: view, withInset: -(newHeight + 16)) // + 16 to match the bottom inset of the table view scrollButton.pin(.bottom, to: .bottom, of: view, withInset: -(newHeight + 16)) // + 16 to match the bottom inset of the table view
didConstrainScrollButton = true didConstrainScrollButton = true
} }
let newContentOffsetY = self.messagesTableView.contentOffset.y + min(lastPageTop, 0) + newHeight - self.messagesTableView.keyboardHeight let newContentOffsetY = max(self.messagesTableView.contentOffset.y + min(lastPageTop, 0) + newHeight - self.messagesTableView.keyboardHeight, 0.0)
self.messagesTableView.contentOffset.y = max(self.messagesTableView.contentOffset.y, newContentOffsetY) self.messagesTableView.contentOffset.y = newContentOffsetY
self.messagesTableView.keyboardHeight = newHeight self.messagesTableView.keyboardHeight = newHeight
self.scrollButton.alpha = self.getScrollButtonOpacity() self.scrollButton.alpha = self.getScrollButtonOpacity()
} }

@ -91,6 +91,24 @@ typedef NS_ENUM(NSUInteger, ConversationUpdateItemType) {
#pragma mark - #pragma mark -
// Always load up to n messages when user arrives.
//
// The smaller this number is, the faster the conversation can display.
// To test, shrink you accessibility font as much as possible, then count how many 1-line system info messages (our
// shortest cells) can fit on screen at a time on an iPhoneX
//
// PERF: we could do less messages on shorter (older, slower) devices
// PERF: we could cache the cell height, since some messages will be much taller.
static const int kYapDatabasePageSize = 250;
// Never show more than n messages in conversation view when user arrives.
static const int kConversationInitialMaxRangeSize = 250;
// Never show more than n messages in conversation view at a time.
static const int kYapDatabaseRangeMaxLength = 250;
#pragma mark -
@interface ConversationViewModel : NSObject @interface ConversationViewModel : NSObject
@property (nonatomic, readonly) ConversationViewState *viewState; @property (nonatomic, readonly) ConversationViewState *viewState;

@ -162,24 +162,6 @@ NS_ASSUME_NONNULL_BEGIN
#pragma mark - #pragma mark -
// Always load up to n messages when user arrives.
//
// The smaller this number is, the faster the conversation can display.
// To test, shrink you accessibility font as much as possible, then count how many 1-line system info messages (our
// shortest cells) can fit on screen at a time on an iPhoneX
//
// PERF: we could do less messages on shorter (older, slower) devices
// PERF: we could cache the cell height, since some messages will be much taller.
static const int kYapDatabasePageSize = 100;
// Never show more than n messages in conversation view when user arrives.
static const int kConversationInitialMaxRangeSize = 100;
// Never show more than n messages in conversation view at a time.
static const int kYapDatabaseRangeMaxLength = 25000;
#pragma mark -
@interface ConversationViewModel () @interface ConversationViewModel ()
@property (nonatomic, weak) id<ConversationViewModelDelegate> delegate; @property (nonatomic, weak) id<ConversationViewModelDelegate> delegate;
@ -214,6 +196,8 @@ static const int kYapDatabaseRangeMaxLength = 25000;
@property (nonatomic) NSArray<id<ConversationViewItem>> *persistedViewItems; @property (nonatomic) NSArray<id<ConversationViewItem>> *persistedViewItems;
@property (nonatomic) NSArray<TSOutgoingMessage *> *unsavedOutgoingMessages; @property (nonatomic) NSArray<TSOutgoingMessage *> *unsavedOutgoingMessages;
@property (nonatomic) BOOL hasUiDatabaseUpdatedExternally;
@end @end
#pragma mark - #pragma mark -
@ -569,9 +553,14 @@ static const int kYapDatabaseRangeMaxLength = 25000;
- (void)uiDatabaseDidUpdateExternally:(NSNotification *)notification - (void)uiDatabaseDidUpdateExternally:(NSNotification *)notification
{ {
OWSAssertIsOnMainThread(); OWSAssertIsOnMainThread();
// External database modifications (e.g. changes from another process such as the SAE) // External database modifications (e.g. changes from another process such as the SAE)
// are "flushed" using touchDbAsync when the app re-enters the foreground. // are "flushed" using touchDbAsync when the app re-enters the foreground.
//
// The NSE will trigger this when we receive a new message through a remote notification.
// In this scenario, touchDbAsync will trigger uiDatabaseDidUpdate, but with a notification
// that does NOT include the recent update from NSE. This flag lets uiDatabaseDidUpdate
// know it needs to expect more updates than those in the notification.
_hasUiDatabaseUpdatedExternally = true;
} }
- (void)uiDatabaseWillUpdate:(NSNotification *)notification - (void)uiDatabaseWillUpdate:(NSNotification *)notification
@ -589,10 +578,12 @@ static const int kYapDatabaseRangeMaxLength = 25000;
YapDatabaseAutoViewConnection *messageDatabaseView = YapDatabaseAutoViewConnection *messageDatabaseView =
[self.uiDatabaseConnection ext:TSMessageDatabaseViewExtensionName]; [self.uiDatabaseConnection ext:TSMessageDatabaseViewExtensionName];
OWSAssertDebug([messageDatabaseView isKindOfClass:[YapDatabaseAutoViewConnection class]]); OWSAssertDebug([messageDatabaseView isKindOfClass:[YapDatabaseAutoViewConnection class]]);
if (![messageDatabaseView hasChangesForGroup:self.thread.uniqueId inNotifications:notifications]) { if (![messageDatabaseView hasChangesForGroup:self.thread.uniqueId inNotifications:notifications] && !self.hasUiDatabaseUpdatedExternally) {
[self.delegate conversationViewModelDidUpdate:ConversationUpdate.minorUpdate]; [self.delegate conversationViewModelDidUpdate:ConversationUpdate.minorUpdate];
return; return;
} }
_hasUiDatabaseUpdatedExternally = false;
__block ConversationMessageMappingDiff *_Nullable diff = nil; __block ConversationMessageMappingDiff *_Nullable diff = nil;
[self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) { [self.uiDatabaseConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {

@ -286,7 +286,7 @@ final class VisibleMessageCell : MessageCell, LinkPreviewViewDelegate {
dateBreakLabel.font = .boldSystemFont(ofSize: Values.verySmallFontSize) dateBreakLabel.font = .boldSystemFont(ofSize: Values.verySmallFontSize)
dateBreakLabel.textColor = Colors.text dateBreakLabel.textColor = Colors.text
dateBreakLabel.textAlignment = .center dateBreakLabel.textAlignment = .center
let date = viewItem.interaction.receivedAtDate() let date = viewItem.interaction.dateForUI()
let description = DateUtil.formatDate(forConversationDateBreaks: date) let description = DateUtil.formatDate(forConversationDateBreaks: date)
dateBreakLabel.text = description dateBreakLabel.text = description
headerView.addSubview(dateBreakLabel) headerView.addSubview(dateBreakLabel)

@ -120,7 +120,7 @@ public struct GalleryDate: Hashable, Comparable, Equatable {
let month: Int let month: Int
init(message: TSMessage) { init(message: TSMessage) {
let date = message.receivedAtDate() let date = message.dateForUI()
self.year = Calendar.current.component(.year, from: date) self.year = Calendar.current.component(.year, from: date)
self.month = Calendar.current.component(.month, from: date) self.month = Calendar.current.component(.month, from: date)

@ -10,6 +10,7 @@ public class Message : NSObject, NSCoding { // NSObject/NSCoding conformance is
public var sender: String? public var sender: String?
public var groupPublicKey: String? public var groupPublicKey: String?
public var openGroupServerMessageID: UInt64? public var openGroupServerMessageID: UInt64?
public var openGroupServerTimestamp: UInt64?
public var ttl: UInt64 { 14 * 24 * 60 * 60 * 1000 } public var ttl: UInt64 { 14 * 24 * 60 * 60 * 1000 }
public var isSelfSendValid: Bool { false } public var isSelfSendValid: Bool { false }
@ -30,6 +31,10 @@ public class Message : NSObject, NSCoding { // NSObject/NSCoding conformance is
if let sentTimestamp = coder.decodeObject(forKey: "sentTimestamp") as! UInt64? { self.sentTimestamp = sentTimestamp } if let sentTimestamp = coder.decodeObject(forKey: "sentTimestamp") as! UInt64? { self.sentTimestamp = sentTimestamp }
if let receivedTimestamp = coder.decodeObject(forKey: "receivedTimestamp") as! UInt64? { self.receivedTimestamp = receivedTimestamp } if let receivedTimestamp = coder.decodeObject(forKey: "receivedTimestamp") as! UInt64? { self.receivedTimestamp = receivedTimestamp }
if let recipient = coder.decodeObject(forKey: "recipient") as! String? { self.recipient = recipient } if let recipient = coder.decodeObject(forKey: "recipient") as! String? { self.recipient = recipient }
if let sender = coder.decodeObject(forKey: "sender") as! String? { self.sender = sender }
if let groupPublicKey = coder.decodeObject(forKey: "groupPublicKey") as! String? { self.groupPublicKey = groupPublicKey }
if let openGroupServerMessageID = coder.decodeObject(forKey: "openGroupServerMessageID") as! UInt64? { self.openGroupServerMessageID = openGroupServerMessageID }
if let openGroupServerTimestamp = coder.decodeObject(forKey: "openGroupServerTimestamp") as! UInt64? { self.openGroupServerTimestamp = openGroupServerTimestamp }
} }
public func encode(with coder: NSCoder) { public func encode(with coder: NSCoder) {
@ -38,6 +43,10 @@ public class Message : NSObject, NSCoding { // NSObject/NSCoding conformance is
coder.encode(sentTimestamp, forKey: "sentTimestamp") coder.encode(sentTimestamp, forKey: "sentTimestamp")
coder.encode(receivedTimestamp, forKey: "receivedTimestamp") coder.encode(receivedTimestamp, forKey: "receivedTimestamp")
coder.encode(recipient, forKey: "recipient") coder.encode(recipient, forKey: "recipient")
coder.encode(sender, forKey: "sender")
coder.encode(groupPublicKey, forKey: "groupPublicKey")
coder.encode(openGroupServerMessageID, forKey: "openGroupServerMessageID")
coder.encode(openGroupServerTimestamp, forKey: "openGroupServerTimestamp")
} }
// MARK: Proto Conversion // MARK: Proto Conversion

@ -19,7 +19,7 @@ public extension TSIncomingMessage {
expiresInSeconds: !isOpenGroupMessage ? expiration : 0, // Ensure we don't ever expire open group messages expiresInSeconds: !isOpenGroupMessage ? expiration : 0, // Ensure we don't ever expire open group messages
quotedMessage: quotedMessage, quotedMessage: quotedMessage,
linkPreview: linkPreview, linkPreview: linkPreview,
serverTimestamp: nil, serverTimestamp: visibleMessage.openGroupServerTimestamp as NSNumber?,
wasReceivedByUD: true wasReceivedByUD: true
) )
result.openGroupServerMessageID = openGroupServerMessageID result.openGroupServerMessageID = openGroupServerMessageID

@ -41,10 +41,10 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value);
@property (nonatomic, readonly) uint64_t receivedAtTimestamp; @property (nonatomic, readonly) uint64_t receivedAtTimestamp;
@property (nonatomic, readonly) BOOL shouldUseServerTime; @property (nonatomic, readonly) BOOL shouldUseServerTime;
- (void)setServerTimestampToReceivedTimestamp:(uint64_t)receivedAtTimestamp;
- (uint64_t)timestampForUI; - (uint64_t)timestampForUI;
- (NSDate *)dateForUI;
- (NSDate *)receivedAtDate; - (NSDate *)receivedAtDate;
- (OWSInteractionType)interactionType; - (OWSInteractionType)interactionType;

@ -169,8 +169,11 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
- (uint64_t)timestampForUI - (uint64_t)timestampForUI
{ {
if (_shouldUseServerTime) { // We always want to show the sent timestamp. In the case of one-on-one, closed group and V2 open group messages we get
return _receivedAtTimestamp; // this from the protobuf. In the case of V1 open group messages we get it from the envelope in which the message is
// wrapped, which gets parsed to `serverTimestamp` in that case.
if ([self isKindOfClass:TSIncomingMessage.class] && ((TSIncomingMessage *)self).isOpenGroupMessage && ((TSIncomingMessage *)self).serverTimestamp != nil) {
return ((TSIncomingMessage *)self).serverTimestamp.unsignedLongLongValue;
} }
return _timestamp; return _timestamp;
} }
@ -180,14 +183,14 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
return self.timestamp; return self.timestamp;
} }
- (void)setServerTimestampToReceivedTimestamp:(uint64_t)receivedAtTimestamp - (NSDate *)dateForUI
{ {
_shouldUseServerTime = YES; return [NSDate ows_dateWithMillisecondsSince1970:self.timestampForUI];
_receivedAtTimestamp = receivedAtTimestamp;
} }
- (NSDate *)receivedAtDate - (NSDate *)receivedAtDate
{ {
// This is only used for sorting threads
return [NSDate ows_dateWithMillisecondsSince1970:self.receivedAtTimestamp]; return [NSDate ows_dateWithMillisecondsSince1970:self.receivedAtTimestamp];
} }
@ -199,13 +202,10 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
// In open groups messages should be sorted by server timestamp. `sortId` represents the order in which messages // In open groups messages should be sorted by server timestamp. `sortId` represents the order in which messages
// were processed. Since in the open group poller we sort messages by their server timestamp, sorting by `sortId` is // were processed. Since in the open group poller we sort messages by their server timestamp, sorting by `sortId` is
// effectively the same as sorting by server timestamp. // effectively the same as sorting by server timestamp.
if ([self isKindOfClass:TSMessage.class] && ((TSMessage *)self).isOpenGroupMessage) { // sortId == serverTimestamp (the sent timestamp) for open group messages.
sortId1 = self.sortId; // sortId == timestamp (the sent timestamp) for one-to-one and closed group messages.
sortId2 = other.sortId; sortId1 = self.sortId;
} else { sortId2 = other.sortId;
sortId1 = self.timestamp;
sortId2 = other.timestamp;
}
if (sortId1 > sortId2) { if (sortId1 > sortId2) {
return NSOrderedDescending; return NSOrderedDescending;
@ -229,6 +229,17 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
(unsigned long)self.timestamp]; (unsigned long)self.timestamp];
} }
- (uint64_t)sortId
{
// We always want to sort on the sent timestamp. In the case of one-on-one, closed group and V2 open group messages we get
// this from the protobuf. In the case of V1 open group messages we get it from the envelope in which the message is
// wrapped, which gets parsed to `serverTimestamp` in that case.
if ([self isKindOfClass:TSIncomingMessage.class] && ((TSIncomingMessage *)self).isOpenGroupMessage && ((TSIncomingMessage *)self).serverTimestamp != nil) {
return ((TSIncomingMessage *)self).serverTimestamp.unsignedLongLongValue;
}
return self.timestamp;
}
- (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction - (void)saveWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{ {
if (!self.uniqueId) { if (!self.uniqueId) {

@ -146,6 +146,9 @@ public enum MessageReceiver {
message.recipient = userPublicKey message.recipient = userPublicKey
message.sentTimestamp = envelope.timestamp message.sentTimestamp = envelope.timestamp
message.receivedTimestamp = NSDate.millisecondTimestamp() message.receivedTimestamp = NSDate.millisecondTimestamp()
if isOpenGroupMessage {
message.openGroupServerTimestamp = envelope.serverTimestamp
}
message.groupPublicKey = groupPublicKey message.groupPublicKey = groupPublicKey
message.openGroupServerMessageID = openGroupMessageServerID message.openGroupServerMessageID = openGroupMessageServerID
// Validate // Validate

@ -32,7 +32,7 @@ public class ThreadViewModel: NSObject {
self.lastMessageText = thread.lastMessageText(transaction: transaction) self.lastMessageText = thread.lastMessageText(transaction: transaction)
let lastInteraction = thread.lastInteractionForInbox(transaction: transaction) let lastInteraction = thread.lastInteractionForInbox(transaction: transaction)
self.lastMessageForInbox = lastInteraction self.lastMessageForInbox = lastInteraction
self.lastMessageDate = lastInteraction?.receivedAtDate() ?? thread.creationDate self.lastMessageDate = lastInteraction?.dateForUI() ?? thread.creationDate
if let contactThread = thread as? TSContactThread { if let contactThread = thread as? TSContactThread {
self.contactIdentifier = contactThread.contactIdentifier() self.contactIdentifier = contactThread.contactIdentifier()

@ -198,6 +198,14 @@ NS_ASSUME_NONNULL_BEGIN
visibleUnseenMessageCount++; visibleUnseenMessageCount++;
interactionAfterUnreadIndicator = interaction; interactionAfterUnreadIndicator = interaction;
if (visibleUnseenMessageCount + 1 >= maxRangeSize) {
// If there are more unseen messages than can be displayed in the
// messages view, show the unread indicator at the top of the
// displayed messages.
*stop = YES;
hasMoreUnseenMessages = YES;
}
}]; }];
if (!interactionAfterUnreadIndicator) { if (!interactionAfterUnreadIndicator) {

Loading…
Cancel
Save