Merge branch 'dev' into light-mode
This commit is contained in:
commit
eb62a1cb42
|
@ -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)",
|
||||
|
|
|
@ -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: ""))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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.";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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" = "یکی از اعضای گروه شما دارای شناسه نامعتبر است";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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无效";
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in New Issue