Merge branch 'dev' into light-mode

This commit is contained in:
nielsandriesse 2020-08-28 11:27:11 +10:00
commit eb62a1cb42
23 changed files with 108 additions and 42 deletions

View File

@ -4100,7 +4100,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 105;
CURRENT_PROJECT_VERSION = 106;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@ -4162,7 +4162,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 105;
CURRENT_PROJECT_VERSION = 106;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = SUQ8J2PCT7;
ENABLE_NS_ASSERTIONS = NO;
@ -4216,7 +4216,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 105;
CURRENT_PROJECT_VERSION = 106;
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
@ -4286,7 +4286,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 105;
CURRENT_PROJECT_VERSION = 106;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
@ -4348,7 +4348,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 105;
CURRENT_PROJECT_VERSION = 106;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
@ -4411,7 +4411,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 105;
CURRENT_PROJECT_VERSION = 106;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = SUQ8J2PCT7;
ENABLE_NS_ASSERTIONS = NO;
@ -4612,7 +4612,7 @@
CODE_SIGN_ENTITLEMENTS = Signal/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 105;
CURRENT_PROJECT_VERSION = 106;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -4680,7 +4680,7 @@
CODE_SIGN_ENTITLEMENTS = Signal/Signal.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 105;
CURRENT_PROJECT_VERSION = 106;
DEVELOPMENT_TEAM = SUQ8J2PCT7;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",

View File

@ -171,8 +171,8 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
guard name.count < 64 else {
return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: ""))
}
guard selectedContacts.count >= 2 else {
return showError(title: NSLocalizedString("vc_create_closed_group_not_enough_group_members_error", comment: ""))
guard selectedContacts.count >= 1 else {
return showError(title: "Please pick at least 1 group member")
}
guard selectedContacts.count < 50 else { // Minus one because we're going to include self later
return showError(title: NSLocalizedString("vc_create_closed_group_too_many_group_members_error", comment: ""))
@ -211,8 +211,8 @@ final class NewClosedGroupVC : BaseVC, UITableViewDataSource, UITableViewDelegat
guard name.count < 64 else {
return showError(title: NSLocalizedString("vc_create_closed_group_group_name_too_long_error", comment: ""))
}
guard selectedContacts.count >= 2 else {
return showError(title: NSLocalizedString("vc_create_closed_group_not_enough_group_members_error", comment: ""))
guard selectedContacts.count >= 1 else {
return showError(title: "Please pick at least 1 group member")
}
guard selectedContacts.count < 10 else { // Minus one because we're going to include self later
return showError(title: NSLocalizedString("vc_create_closed_group_too_many_group_members_error", comment: ""))

View File

@ -549,7 +549,7 @@ const CGFloat kRemotelySourcedContentRowSpacing = 4;
}
} else {
OWSContactsManager *contactsManager = Environment.shared.contactsManager;
__block NSString *quotedAuthor = [contactsManager contactOrProfileNameForPhoneIdentifier:self.quotedMessage.authorId];
__block NSString *quotedAuthor = [SSKEnvironment.shared.profileManager profileNameForRecipientWithID:self.quotedMessage.authorId] ?: [contactsManager contactOrProfileNameForPhoneIdentifier:self.quotedMessage.authorId];
if (quotedAuthor == self.quotedMessage.authorId) {
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {

View File

@ -3607,7 +3607,6 @@ typedef enum : NSUInteger {
// and won't update the UI state immediately.
- (void)didScrollToBottom
{
id<ConversationViewItem> _Nullable lastVisibleViewItem = [self.viewItems lastObject];
if (lastVisibleViewItem) {
uint64_t lastVisibleSortId = lastVisibleViewItem.interaction.sortId;

View File

@ -63,8 +63,16 @@ NS_ASSUME_NONNULL_BEGIN
interactionIndexMap[viewItem.interaction.uniqueId] = @(i);
[interactionIds addObject:viewItem.interaction.uniqueId];
if (viewItem.unreadIndicator != nil) {
_unreadIndicatorIndex = @(i);
if (viewItem.unreadIndicator != nil && [viewItem.interaction conformsToProtocol:@protocol(OWSReadTracking)]) {
id<OWSReadTracking> interaction = (id<OWSReadTracking>)viewItem.interaction;
// Under normal circumstances !interaction.read should always evaluate to true at this point, but
// there is a bug that can somehow cause it to be false leading to conversations permanently being
// stuck with "unread" messages.
if (!interaction.read) {
_unreadIndicatorIndex = @(i);
}
}
}
_interactionIndexMap = [interactionIndexMap copy];

View File

@ -2638,7 +2638,6 @@
"vc_create_closed_group_empty_state_button_title" = "Session starten";
"vc_create_closed_group_group_name_missing_error" = "Bitte geben Sie einen Gruppennamen ein.";
"vc_create_closed_group_group_name_too_long_error" = "Bitte geben Sie einen kürzeren Gruppennamen ein.";
"vc_create_closed_group_not_enough_group_members_error" = "Bitte wählen Sie mindestens zwei Gruppenmitglieder aus.";
"vc_create_closed_group_too_many_group_members_error" = "Eine geschlossene Gruppe kann maximal zehn Mitglieder haben.";
"vc_create_closed_group_invalid_session_id_error" = "Ein Mitglied Ihrer Gruppe hat eine ungültige Session ID.";

View File

@ -2647,7 +2647,6 @@
"vc_create_closed_group_empty_state_button_title" = "Start a Session";
"vc_create_closed_group_group_name_missing_error" = "Please enter a group name";
"vc_create_closed_group_group_name_too_long_error" = "Please enter a shorter group name";
"vc_create_closed_group_not_enough_group_members_error" = "Please pick at least 2 group members";
"vc_create_closed_group_too_many_group_members_error" = "A closed group cannot have more than 10 members";
"vc_create_closed_group_invalid_session_id_error" = "One of the members of your group has an invalid Session ID";

View File

@ -2638,7 +2638,6 @@
"vc_create_closed_group_empty_state_button_title" = "Empezar una Session";
"vc_create_closed_group_group_name_missing_error" = "Por favor, ingresa un nombre de grupo";
"vc_create_closed_group_group_name_too_long_error" = "Por favor, ingresa un nombre de grupo más corto";
"vc_create_closed_group_not_enough_group_members_error" = "Por favor, elige al menos 2 miembros del grupo";
"vc_create_closed_group_too_many_group_members_error" = "Un grupo cerrado no puede tener más de 10 miembros";
"vc_create_closed_group_invalid_session_id_error" = "Uno de los miembros de tu grupo tiene un ID de Session no válido";

View File

@ -2638,7 +2638,6 @@
"vc_create_closed_group_empty_state_button_title" = "شروع Session";
"vc_create_closed_group_group_name_missing_error" = "لطفا یک نام گروه وارد کنید";
"vc_create_closed_group_group_name_too_long_error" = "لطفا نام گروه کوتاه‌تری وارد کنید";
"vc_create_closed_group_not_enough_group_members_error" = "لطفا حداقل ۲ عضو برای گروه انتخاب کنید";
"vc_create_closed_group_too_many_group_members_error" = "یک گروه خصوصی نمی‌تواند بیش از ۱۰ عضو داشته باشد";
"vc_create_closed_group_invalid_session_id_error" = "یکی از اعضای گروه شما دارای شناسه نامعتبر است";

View File

@ -2648,7 +2648,6 @@
"vc_create_closed_group_empty_state_button_title" = "Démarrer une session";
"vc_create_closed_group_group_name_missing_error" = "Veuillez saisir un nom de groupe";
"vc_create_closed_group_group_name_too_long_error" = "Veuillez saisir un nom de groupe plus court";
"vc_create_closed_group_not_enough_group_members_error" = "Veuillez sélectionner au moins 2 membres";
"vc_create_closed_group_too_many_group_members_error" = "Un groupe privé ne peut pas avoir plus de 10 membres";
"vc_create_closed_group_invalid_session_id_error" = "Un des membres de votre groupe a un Session ID non valide";

View File

@ -2638,7 +2638,6 @@
"vc_create_closed_group_empty_state_button_title" = "Inizia una sessione";
"vc_create_closed_group_group_name_missing_error" = "Inserisci un nome per il gruppo";
"vc_create_closed_group_group_name_too_long_error" = "Inserisci un nome gruppo più breve";
"vc_create_closed_group_not_enough_group_members_error" = "Scegli almeno 2 membri del gruppo";
"vc_create_closed_group_too_many_group_members_error" = "Un gruppo chiuso non può avere più di 10 membri";
"vc_create_closed_group_invalid_session_id_error" = "Uno dei membri del tuo gruppo ha una Sessione ID non valido";

View File

@ -2638,7 +2638,6 @@
"vc_create_closed_group_empty_state_button_title" = "Iniciar uma sessão";
"vc_create_closed_group_group_name_missing_error" = "Digite um nome de grupo";
"vc_create_closed_group_group_name_too_long_error" = "Digite um nome de grupo mais curto";
"vc_create_closed_group_not_enough_group_members_error" = "Escolha pelo menos 2 membros do grupo";
"vc_create_closed_group_too_many_group_members_error" = "Um grupo fechado não pode ter mais de 10 membros";
"vc_create_closed_group_invalid_session_id_error" = "Um dos membros do seu grupo tem um ID Session inválido";

View File

@ -2570,7 +2570,7 @@
"view_fake_chat_bubble_5" = "Друзья не позволят друзьям использовать ненадежные мессенджеры. Пользуйтесь на здоровье.";
"vc_register_title" = "Познакомьтесь со своим Session ID";
"vc_register_explanation" = "Ваш Session ID - это уникальный адрес, который могут использовать другие люди для связи с вами при помощи Session. Поскольку ваш Session ID никак не связан с вашей настоящей личностью, он по определению является полностью анонимным и конфиденциальным.";
"vc_register_explanation" = "Ваш Session ID - это уникальный адрес, который другие пользователи могут использовать для связи с вами при помощи Session. Поскольку ваш Session ID никак не связан с вашей настоящей личностью, он по определению является полностью анонимным и конфиденциальным.";
"vc_register_public_key_copied_message" = "Скопировано в буфер обмена";
"vc_restore_title" = "Восстановите свой аккаунт";
@ -2638,7 +2638,6 @@
"vc_create_closed_group_empty_state_button_title" = "Начать Сессию";
"vc_create_closed_group_group_name_missing_error" = "Пожалуйста, введите название группы";
"vc_create_closed_group_group_name_too_long_error" = "Пожалуйста, введите более короткое имя группы";
"vc_create_closed_group_not_enough_group_members_error" = "Пожалуйста, выберите как минимум 2 участников группы";
"vc_create_closed_group_too_many_group_members_error" = "В закрытой группе не может быть больше 10 участников";
"vc_create_closed_group_invalid_session_id_error" = "Один из участников вашей группы имеет недопустимый Session ID";

View File

@ -2638,7 +2638,6 @@
"vc_create_closed_group_empty_state_button_title" = "开始对话";
"vc_create_closed_group_group_name_missing_error" = "请输入群组名称";
"vc_create_closed_group_group_name_too_long_error" = "请输入较短的群组名称";
"vc_create_closed_group_not_enough_group_members_error" = "请选择至少2位群组成员";
"vc_create_closed_group_too_many_group_members_error" = "私密群组成员不得超过10个";
"vc_create_closed_group_invalid_session_id_error" = "您群组中的一位成员的Session ID无效";

View File

@ -348,7 +348,12 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
OWSFailDebug(@"Unexpected object in unseen messages: %@", [object class]);
return;
}
[messages addObject:(id<OWSReadTracking>)object];
id<OWSReadTracking> unread = (id<OWSReadTracking>)object;
if (unread.read) {
[LKLogger print:@"Found an already read message in the * unseen * messages list."];
return;
}
[messages addObject:unread];
}];
return [messages copy];
@ -356,7 +361,24 @@ ConversationColorName const kConversationColorName_Default = ConversationColorNa
- (NSUInteger)unreadMessageCountWithTransaction:(YapDatabaseReadTransaction *)transaction
{
return [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInGroup:self.uniqueId];
__block NSUInteger count = 0;
YapDatabaseViewTransaction *unreadMessages = [transaction ext:TSUnreadDatabaseViewExtensionName];
[unreadMessages enumerateKeysAndObjectsInGroup:self.uniqueId
usingBlock:^(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) {
if (![object conformsToProtocol:@protocol(OWSReadTracking)]) {
OWSFailDebug(@"Unexpected object in unread messages: %@", [object class]);
return;
}
id<OWSReadTracking> unread = (id<OWSReadTracking>)object;
if (unread.read) {
[LKLogger print:@"Found an already read message in the * unread * messages list."];
return;
}
count += 1;
}];
return count;
}
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction

View File

@ -12,10 +12,10 @@ public final class FileServerAPI : DotNetAPI {
public static let maxFileSize = 10_000_000 // 10 MB
/// The file server has a file size limit of `maxFileSize`, which the Service Nodes try to enforce as well. However, the limit applied by the Service Nodes
/// is on the **HTTP request** and not the file size. Because of onion request encryption, a file that's about 4 MB will result in a request that's about 18 MB.
/// On average the multiplier appears to be about 4.4, so when checking whether the file will exceed the file size limit when uploading a file we just divide
/// On average the multiplier appears to be about 6, so when checking whether the file will exceed the file size limit when uploading a file we just divide
/// the size of the file by this number. The alternative would be to actually check the size of the HTTP request but that's only possible after proof of work
/// has been calculated and the onion request encryption has happened, which takes several seconds.
public static let fileSizeORMultiplier = 4.4
public static let fileSizeORMultiplier: Double = 6
@objc public static let server = "https://file.getsession.org"

View File

@ -122,13 +122,16 @@ public final class PublicChatAPI : DotNetAPI {
return rawMessages.flatMap { message in
let isDeleted = (message["is_deleted"] as? Int == 1)
guard !isDeleted else { return nil }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
guard let annotations = message["annotations"] as? [JSON], let annotation = annotations.first(where: { $0["type"] as? String == publicChatMessageType }), let value = annotation["value"] as? JSON,
let serverID = message["id"] as? UInt64, let hexEncodedSignatureData = value["sig"] as? String, let signatureVersion = value["sigver"] as? UInt64,
let body = message["text"] as? String, let user = message["user"] as? JSON, let hexEncodedPublicKey = user["username"] as? String,
let timestamp = value["timestamp"] as? UInt64 else {
let timestamp = value["timestamp"] as? UInt64, let dateAsString = message["created_at"] as? String, let date = dateFormatter.date(from: dateAsString) else {
print("[Loki] Couldn't parse message for public chat channel with ID: \(channel) on server: \(server) from: \(message).")
return nil
}
let serverTimestamp = UInt64(date.timeIntervalSince1970) * 1000
var profilePicture: PublicChatMessage.ProfilePicture? = nil
let displayName = user["name"] as? String ?? NSLocalizedString("Anonymous", comment: "")
if let userAnnotations = user["annotations"] as? [JSON], let profilePictureAnnotation = userAnnotations.first(where: { $0["type"] as? String == profilePictureType }),
@ -168,7 +171,7 @@ public final class PublicChatAPI : DotNetAPI {
width: width, height: height, caption: caption, url: url, linkPreviewURL: linkPreviewURL, linkPreviewTitle: linkPreviewTitle)
}
let result = PublicChatMessage(serverID: serverID, senderPublicKey: hexEncodedPublicKey, displayName: displayName, profilePicture: profilePicture,
body: body, type: publicChatMessageType, timestamp: timestamp, quote: quote, attachments: attachments, signature: signature)
body: body, type: publicChatMessageType, timestamp: timestamp, quote: quote, attachments: attachments, signature: signature, serverTimestamp: serverTimestamp)
guard result.hasValidSignature() else {
print("[Loki] Ignoring public chat message with invalid signature.")
return nil
@ -182,7 +185,7 @@ public final class PublicChatAPI : DotNetAPI {
return nil
}
return result
}.sorted { $0.timestamp < $1.timestamp }
}.sorted { $0.serverTimestamp < $1.serverTimestamp}
}
}
}.handlingInvalidAuthTokenIfNeeded(for: server)
@ -217,7 +220,7 @@ public final class PublicChatAPI : DotNetAPI {
throw DotNetAPIError.parsingFailed
}
let timestamp = UInt64(date.timeIntervalSince1970) * 1000
return PublicChatMessage(serverID: serverID, senderPublicKey: getUserHexEncodedPublicKey(), displayName: displayName, profilePicture: signedMessage.profilePicture, body: body, type: publicChatMessageType, timestamp: timestamp, quote: signedMessage.quote, attachments: signedMessage.attachments, signature: signedMessage.signature)
return PublicChatMessage(serverID: serverID, senderPublicKey: getUserHexEncodedPublicKey(), displayName: displayName, profilePicture: signedMessage.profilePicture, body: body, type: publicChatMessageType, timestamp: timestamp, quote: signedMessage.quote, attachments: signedMessage.attachments, signature: signedMessage.signature, serverTimestamp: timestamp)
}
}
}.handlingInvalidAuthTokenIfNeeded(for: server)

View File

@ -13,6 +13,8 @@ public final class PublicChatMessage : NSObject {
public let quote: Quote?
public var attachments: [Attachment] = []
public let signature: Signature?
/// - Note: Used for sorting.
public let serverTimestamp: UInt64
@objc(serverID)
public var objc_serverID: UInt64 { return serverID ?? 0 }
@ -72,7 +74,7 @@ public final class PublicChatMessage : NSObject {
}
// MARK: Initialization
public init(serverID: UInt64?, senderPublicKey: String, displayName: String, profilePicture: ProfilePicture?, body: String, type: String, timestamp: UInt64, quote: Quote?, attachments: [Attachment], signature: Signature?) {
public init(serverID: UInt64?, senderPublicKey: String, displayName: String, profilePicture: ProfilePicture?, body: String, type: String, timestamp: UInt64, quote: Quote?, attachments: [Attachment], signature: Signature?, serverTimestamp: UInt64) {
self.serverID = serverID
self.senderPublicKey = senderPublicKey
self.displayName = displayName
@ -83,10 +85,11 @@ public final class PublicChatMessage : NSObject {
self.quote = quote
self.attachments = attachments
self.signature = signature
self.serverTimestamp = serverTimestamp
super.init()
}
@objc public convenience init(senderPublicKey: String, displayName: String, body: String, type: String, timestamp: UInt64, quotedMessageTimestamp: UInt64, quoteePublicKey: String?, quotedMessageBody: String?, quotedMessageServerID: UInt64, signatureData: Data?, signatureVersion: UInt64) {
@objc public convenience init(senderPublicKey: String, displayName: String, body: String, type: String, timestamp: UInt64, quotedMessageTimestamp: UInt64, quoteePublicKey: String?, quotedMessageBody: String?, quotedMessageServerID: UInt64, signatureData: Data?, signatureVersion: UInt64, serverTimestamp: UInt64) {
let quote: Quote?
if quotedMessageTimestamp != 0, let quoteeHexEncodedPublicKey = quoteePublicKey, let quotedMessageBody = quotedMessageBody {
let quotedMessageServerID = (quotedMessageServerID != 0) ? quotedMessageServerID : nil
@ -100,7 +103,7 @@ public final class PublicChatMessage : NSObject {
} else {
signature = nil
}
self.init(serverID: nil, senderPublicKey: senderPublicKey, displayName: displayName, profilePicture: nil, body: body, type: type, timestamp: timestamp, quote: quote, attachments: [], signature: signature)
self.init(serverID: nil, senderPublicKey: senderPublicKey, displayName: displayName, profilePicture: nil, body: body, type: type, timestamp: timestamp, quote: quote, attachments: [], signature: signature, serverTimestamp: serverTimestamp)
}
// MARK: Crypto
@ -115,7 +118,7 @@ public final class PublicChatMessage : NSObject {
return nil
}
let signature = Signature(data: signatureData, version: signatureVersion)
return PublicChatMessage(serverID: serverID, senderPublicKey: senderPublicKey, displayName: displayName, profilePicture: profilePicture, body: body, type: type, timestamp: timestamp, quote: quote, attachments: attachments, signature: signature)
return PublicChatMessage(serverID: serverID, senderPublicKey: senderPublicKey, displayName: displayName, profilePicture: profilePicture, body: body, type: type, timestamp: timestamp, quote: quote, attachments: attachments, signature: signature, serverTimestamp: serverTimestamp)
}
internal func hasValidSignature() -> Bool {

View File

@ -75,7 +75,7 @@ public final class PublicChatPoller : NSObject {
}
*/
// Sorting the messages by timestamp before importing them fixes an issue where messages that quote older messages can't find those older messages
messages.sorted { $0.timestamp < $1.timestamp }.forEach { message in
messages.sorted { $0.serverTimestamp < $1.serverTimestamp }.forEach { message in
var wasSentByCurrentUser = false
OWSPrimaryStorage.shared().dbReadConnection.read { transaction in
wasSentByCurrentUser = LokiDatabaseUtilities.isUserLinkedDevice(message.senderPublicKey, transaction: transaction)

View File

@ -5,6 +5,7 @@
#import "TSInteraction.h"
#import "TSDatabaseSecondaryIndexes.h"
#import "TSThread.h"
#import "TSGroupThread.h"
#import <SessionCoreKit/NSDate+OWS.h>
#import <SessionServiceKit/SessionServiceKit-Swift.h>
@ -188,10 +189,21 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
{
OWSAssertDebug(other);
// Loki: Sort the messages by the sender's timestamp (Signal uses sortId)
// Sort the messages by the sender's timestamp (Signal uses sortId)
uint64_t sortId1 = self.timestamp;
uint64_t sortId2 = other.timestamp;
// In open groups messages should be sorted by their 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
// effectively the same as sorting by server timestamp.
if (self.thread.isGroupThread) {
TSGroupThread *thread = (TSGroupThread *)self.thread;
if (thread.isPublicChat) {
sortId1 = self.sortId;
sortId2 = other.sortId;
}
}
if (sortId1 > sortId2) {
return NSOrderedDescending;
} else if (sortId1 < sortId2) {

View File

@ -1043,7 +1043,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
}
NSString *body = (message.body != nil && message.body.length > 0) ? message.body : [NSString stringWithFormat:@"%@", @(message.timestamp)]; // Workaround for the fact that the back-end doesn't accept messages without a body
LKPublicChatMessage *groupMessage = [[LKPublicChatMessage alloc] initWithSenderPublicKey:userPublicKey displayName:displayName body:body type:LKPublicChatAPI.publicChatMessageType
timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteePublicKey:quoteePublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0];
timestamp:message.timestamp quotedMessageTimestamp:quoteID quoteePublicKey:quoteePublicKey quotedMessageBody:quote.body quotedMessageServerID:quotedMessageServerID signatureData:nil signatureVersion:0 serverTimestamp:0];
OWSLinkPreview *linkPreview = message.linkPreview;
if (linkPreview != nil) {
TSAttachmentStream *attachment = [TSAttachmentStream fetchObjectWithUniqueID:linkPreview.imageAttachmentId];

View File

@ -18,6 +18,7 @@
#import "TSThread.h"
#import "UIImage+OWS.h"
#import <YapDatabase/YapDatabase.h>
#import <SessionServiceKit/SessionServiceKit-Swift.h>
NS_ASSUME_NONNULL_BEGIN
@ -65,6 +66,30 @@ NS_ASSUME_NONNULL_BEGIN
- (NSUInteger)unreadMessagesCount
{
__block NSUInteger count = 0;
[LKStorage readWithBlock:^(YapDatabaseReadTransaction *transaction) {
YapDatabaseViewTransaction *unreadMessages = [transaction ext:TSUnreadDatabaseViewExtensionName];
NSArray<NSString *> *allGroups = [unreadMessages allGroups];
for (NSString *groupID in allGroups) {
[unreadMessages enumerateKeysAndObjectsInGroup:groupID
usingBlock:^(NSString *collection, NSString *key, id object, NSUInteger index, BOOL *stop) {
if (![object conformsToProtocol:@protocol(OWSReadTracking)]) {
OWSFailDebug(@"Unexpected object in unread messages: %@", [object class]);
return;
}
id<OWSReadTracking> unread = (id<OWSReadTracking>)object;
if (unread.read) {
[LKLogger print:@"Found an already read message in the * unread * messages list."];
return;
}
count += 1;
}];
}
}];
return count;
__block NSUInteger numberOfItems;
[self.dbConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
numberOfItems = [[transaction ext:TSUnreadDatabaseViewExtensionName] numberOfItemsInAllGroups];

View File

@ -491,7 +491,10 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
return;
}
OWSAssertDebug(!possiblyRead.read);
// Under normal circumstances !possiblyRead.read should always evaluate to true at this point, but
// there is a bug that can somehow cause it to be false leading to conversations permanently being
// stuck with "unread" messages.
OWSAssertDebug(possiblyRead.expireStartedAt == 0);
if (!possiblyRead.read) {
[newlyReadList addObject:possiblyRead];