Merge branch 'fix/tweaks-to-config-message-generation' of https://github.com/mpretty-cyro/session-ios into bug-fix-for-call
This commit is contained in:
commit
fdde7e4fe0
|
@ -177,6 +177,7 @@
|
|||
7BC01A42241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 7BC01A3B241F40AB00BC7C55 /* SessionNotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
7BC707F227290ACB002817AD /* SessionCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BC707F127290ACB002817AD /* SessionCallManager.swift */; };
|
||||
7BCD116C27016062006330F1 /* WebRTCSession+DataChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */; };
|
||||
7BD477A827EC39F5004E2822 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD477A727EC39F5004E2822 /* Atomic.swift */; };
|
||||
7BDCFC08242186E700641C39 /* NotificationServiceExtensionContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */; };
|
||||
7BDCFC0B2421EB7600641C39 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = B6F509951AA53F760068F56A /* Localizable.strings */; };
|
||||
7BFD1A8A2745C4F000FB91B9 /* Permissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFD1A892745C4F000FB91B9 /* Permissions.swift */; };
|
||||
|
@ -1198,6 +1199,7 @@
|
|||
7BC01A3F241F40AB00BC7C55 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
7BC707F127290ACB002817AD /* SessionCallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCallManager.swift; sourceTree = "<group>"; };
|
||||
7BCD116B27016062006330F1 /* WebRTCSession+DataChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebRTCSession+DataChannel.swift"; sourceTree = "<group>"; };
|
||||
7BD477A727EC39F5004E2822 /* Atomic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
|
||||
7BDCFC0424206E7300641C39 /* SessionNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SessionNotificationServiceExtension.entitlements; sourceTree = "<group>"; };
|
||||
7BDCFC07242186E700641C39 /* NotificationServiceExtensionContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationServiceExtensionContext.swift; sourceTree = "<group>"; };
|
||||
7BFD1A892745C4F000FB91B9 /* Permissions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Permissions.swift; sourceTree = "<group>"; };
|
||||
|
@ -2433,6 +2435,7 @@
|
|||
B8A582B0258C66C900AFD84C /* General */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7BD477A727EC39F5004E2822 /* Atomic.swift */,
|
||||
7BAF54D527ACD0E2003D12F8 /* ReusableView.swift */,
|
||||
7BAF54D627ACD0E3003D12F8 /* String+Localization.swift */,
|
||||
7BAF54D727ACD0E3003D12F8 /* UITableView+ReusableView.swift */,
|
||||
|
@ -4796,6 +4799,7 @@
|
|||
C3A71F892558BA9F0043A11F /* Mnemonic.swift in Sources */,
|
||||
B8F5F58325EC94A6003BF8D4 /* Collection+Subscripting.swift in Sources */,
|
||||
C33FDEF8255A656D00E217F9 /* Promise+Delaying.swift in Sources */,
|
||||
7BD477A827EC39F5004E2822 /* Atomic.swift in Sources */,
|
||||
B8BC00C0257D90E30032E807 /* General.swift in Sources */,
|
||||
C32C5A24256DB7DB003C73A2 /* SNUserDefaults.swift in Sources */,
|
||||
C3D9E41F25676C870040E4F3 /* OWSPrimaryStorageProtocol.swift in Sources */,
|
||||
|
|
|
@ -283,49 +283,46 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
|
|||
let linkPreviewDraft = snInputView.linkPreviewInfo?.draft
|
||||
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
|
||||
|
||||
Storage.write(with: { transaction in
|
||||
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
|
||||
for: self.thread,
|
||||
with: transaction,
|
||||
isNewThread: !oldThreadShouldBeVisible,
|
||||
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
|
||||
)
|
||||
.map { [weak self] _ in
|
||||
self?.viewModel.appendUnsavedOutgoingTextMessage(tsMessage)
|
||||
|
||||
Storage.write(with: { transaction in
|
||||
message.linkPreview = VisibleMessage.LinkPreview.from(linkPreviewDraft, using: transaction)
|
||||
}, completion: { [weak self] in
|
||||
tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview)
|
||||
|
||||
Storage.shared.write(
|
||||
with: { transaction in
|
||||
tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction)
|
||||
},
|
||||
completion: { [weak self] in
|
||||
// At this point the TSOutgoingMessage should have its link preview set, so we can scroll to the bottom knowing
|
||||
// the height of the new message cell
|
||||
self?.scrollToBottom(isAnimated: false)
|
||||
}
|
||||
)
|
||||
|
||||
Storage.shared.write { transaction in
|
||||
MessageSender.send(message, with: [], in: thread, using: transaction as! YapDatabaseReadWriteTransaction)
|
||||
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
|
||||
for: self.thread,
|
||||
isNewThread: !oldThreadShouldBeVisible,
|
||||
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
|
||||
)
|
||||
.map { [weak self] _ in
|
||||
self?.viewModel.appendUnsavedOutgoingTextMessage(tsMessage)
|
||||
|
||||
Storage.write(with: { transaction in
|
||||
message.linkPreview = VisibleMessage.LinkPreview.from(linkPreviewDraft, using: transaction)
|
||||
}, completion: { [weak self] in
|
||||
tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview)
|
||||
|
||||
Storage.shared.write(
|
||||
with: { transaction in
|
||||
tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction)
|
||||
},
|
||||
completion: { [weak self] in
|
||||
// At this point the TSOutgoingMessage should have its link preview set, so we can scroll to the bottom knowing
|
||||
// the height of the new message cell
|
||||
self?.scrollToBottom(isAnimated: false)
|
||||
}
|
||||
|
||||
self?.handleMessageSent()
|
||||
})
|
||||
}
|
||||
|
||||
// Show an error indicating that approving the thread failed
|
||||
promise.catch(on: DispatchQueue.main) { [weak self] _ in
|
||||
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
||||
self?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
promise.retainUntilComplete()
|
||||
})
|
||||
)
|
||||
|
||||
Storage.shared.write { transaction in
|
||||
MessageSender.send(message, with: [], in: thread, using: transaction as! YapDatabaseReadWriteTransaction)
|
||||
}
|
||||
|
||||
self?.handleMessageSent()
|
||||
})
|
||||
}
|
||||
|
||||
// Show an error indicating that approving the thread failed
|
||||
promise.catch(on: DispatchQueue.main) { [weak self] _ in
|
||||
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
||||
self?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
promise.retainUntilComplete()
|
||||
}
|
||||
|
||||
func sendAttachments(_ attachments: [SignalAttachment], with text: String, onComplete: (() -> ())? = nil) {
|
||||
|
@ -349,44 +346,41 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
|
|||
let oldThreadShouldBeVisible: Bool = thread.shouldBeVisible
|
||||
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
|
||||
|
||||
Storage.write(with: { transaction in
|
||||
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
|
||||
for: self.thread,
|
||||
with: transaction,
|
||||
isNewThread: !oldThreadShouldBeVisible,
|
||||
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
|
||||
)
|
||||
.map { [weak self] _ in
|
||||
Storage.write(
|
||||
with: { transaction in
|
||||
tsMessage.save(with: transaction)
|
||||
// The new message cell is inserted at this point, but the TSOutgoingMessage doesn't have its attachment yet
|
||||
},
|
||||
completion: { [weak self] in
|
||||
Storage.write(with: { transaction in
|
||||
MessageSender.send(message, with: attachments, in: thread, using: transaction)
|
||||
}, completion: { [weak self] in
|
||||
// At this point the TSOutgoingMessage should have its attachments set, so we can scroll to the bottom knowing
|
||||
// the height of the new message cell
|
||||
self?.scrollToBottom(isAnimated: false)
|
||||
})
|
||||
self?.handleMessageSent()
|
||||
|
||||
// Attachment successfully sent - dismiss the screen
|
||||
onComplete?()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Show an error indicating that approving the thread failed
|
||||
promise.catch(on: DispatchQueue.main) { [weak self] _ in
|
||||
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
||||
self?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
|
||||
for: self.thread,
|
||||
isNewThread: !oldThreadShouldBeVisible,
|
||||
timestamp: (sentTimestamp - 1) // Set 1ms earlier as this is used for sorting
|
||||
)
|
||||
.map { [weak self] _ in
|
||||
Storage.write(
|
||||
with: { transaction in
|
||||
tsMessage.save(with: transaction)
|
||||
// The new message cell is inserted at this point, but the TSOutgoingMessage doesn't have its attachment yet
|
||||
},
|
||||
completion: { [weak self] in
|
||||
Storage.write(with: { transaction in
|
||||
MessageSender.send(message, with: attachments, in: thread, using: transaction)
|
||||
}, completion: { [weak self] in
|
||||
// At this point the TSOutgoingMessage should have its attachments set, so we can scroll to the bottom knowing
|
||||
// the height of the new message cell
|
||||
self?.scrollToBottom(isAnimated: false)
|
||||
})
|
||||
self?.handleMessageSent()
|
||||
|
||||
promise.retainUntilComplete()
|
||||
})
|
||||
// Attachment successfully sent - dismiss the screen
|
||||
onComplete?()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Show an error indicating that approving the thread failed
|
||||
promise.catch(on: DispatchQueue.main) { [weak self] _ in
|
||||
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
||||
self?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
promise.retainUntilComplete()
|
||||
}
|
||||
|
||||
func handleMessageSent() {
|
||||
|
@ -1128,7 +1122,7 @@ extension ConversationVC: UIDocumentInteractionControllerDelegate {
|
|||
|
||||
extension ConversationVC {
|
||||
|
||||
fileprivate func approveMessageRequestIfNeeded(for thread: TSThread?, with transaction: YapDatabaseReadWriteTransaction, isNewThread: Bool, timestamp: UInt64) -> Promise<Void> {
|
||||
fileprivate func approveMessageRequestIfNeeded(for thread: TSThread?, isNewThread: Bool, timestamp: UInt64) -> Promise<Void> {
|
||||
guard let contactThread: TSContactThread = thread as? TSContactThread else { return Promise.value(()) }
|
||||
|
||||
// If the contact doesn't exist then we should create it so we can store the 'isApproved' state
|
||||
|
@ -1159,7 +1153,17 @@ extension ConversationVC {
|
|||
}
|
||||
|
||||
return promise
|
||||
.then { MessageSender.sendNonDurably(messageRequestResponse, in: contactThread, using: transaction) }
|
||||
.then { _ -> Promise<Void> in
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
Storage.writeSync { transaction in
|
||||
MessageSender.sendNonDurably(messageRequestResponse, in: contactThread, using: transaction)
|
||||
.done { seal.fulfill(()) }
|
||||
.catch { _ in seal.fulfill(()) } // Fulfill even if this failed; the configuration in the swarm should be at most 2 days old
|
||||
.retainUntilComplete()
|
||||
}
|
||||
|
||||
return promise
|
||||
}
|
||||
.map { _ in
|
||||
if self?.presentedViewController is ModalActivityIndicatorViewController {
|
||||
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
|
||||
|
@ -1168,9 +1172,11 @@ extension ConversationVC {
|
|||
}
|
||||
.map { _ in
|
||||
// Default 'didApproveMe' to true for the person approving the message request
|
||||
contact.isApproved = true
|
||||
contact.didApproveMe = (contact.didApproveMe || !isNewThread)
|
||||
Storage.shared.setContact(contact, using: transaction)
|
||||
Storage.write { transaction in
|
||||
contact.isApproved = true
|
||||
contact.didApproveMe = (contact.didApproveMe || !isNewThread)
|
||||
Storage.shared.setContact(contact, using: transaction)
|
||||
}
|
||||
|
||||
// Hide the 'messageRequestView' since the request has been approved and force a config
|
||||
// sync to propagate the contact approval state (both must run on the main thread)
|
||||
|
@ -1208,30 +1214,27 @@ extension ConversationVC {
|
|||
|
||||
// Send a sync message with the details of the contact
|
||||
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
|
||||
appDelegate.forceSyncConfigurationNowIfNeeded(with: transaction).retainUntilComplete()
|
||||
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func acceptMessageRequest() {
|
||||
Storage.write { transaction in
|
||||
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
|
||||
for: self.thread,
|
||||
with: transaction,
|
||||
isNewThread: false,
|
||||
timestamp: NSDate.millisecondTimestamp()
|
||||
)
|
||||
|
||||
// Show an error indicating that approving the thread failed
|
||||
promise.catch(on: DispatchQueue.main) { [weak self] _ in
|
||||
let alert = UIAlertController(title: "Session", message: NSLocalizedString("MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE", comment: ""), preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
||||
self?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
promise.retainUntilComplete()
|
||||
let promise: Promise<Void> = self.approveMessageRequestIfNeeded(
|
||||
for: self.thread,
|
||||
isNewThread: false,
|
||||
timestamp: NSDate.millisecondTimestamp()
|
||||
)
|
||||
|
||||
// Show an error indicating that approving the thread failed
|
||||
promise.catch(on: DispatchQueue.main) { [weak self] _ in
|
||||
let alert = UIAlertController(title: "Session", message: NSLocalizedString("MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE", comment: ""), preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
||||
self?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
promise.retainUntilComplete()
|
||||
}
|
||||
|
||||
@objc func deleteMessageRequest() {
|
||||
|
|
|
@ -144,10 +144,11 @@ extension AppDelegate {
|
|||
guard Storage.shared.getUser()?.name != nil else { return }
|
||||
let userDefaults = UserDefaults.standard
|
||||
let lastSync = userDefaults[.lastConfigurationSync] ?? .distantPast
|
||||
guard Date().timeIntervalSince(lastSync) > 7 * 24 * 60 * 60,
|
||||
let configurationMessage = ConfigurationMessage.getCurrent() else { return } // Sync every 2 days
|
||||
guard Date().timeIntervalSince(lastSync) > 7 * 24 * 60 * 60 else { return } // Sync every 2 days
|
||||
let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey())
|
||||
Storage.shared.write { transaction in
|
||||
Storage.write { transaction in
|
||||
guard let configurationMessage = ConfigurationMessage.getCurrent(with: transaction) else { return }
|
||||
|
||||
let job = MessageSendJob(message: configurationMessage, destination: destination)
|
||||
JobQueue.shared.add(job, using: transaction)
|
||||
}
|
||||
|
@ -159,14 +160,17 @@ extension AppDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func forceSyncConfigurationNowIfNeeded(with transaction: YapDatabaseReadWriteTransaction? = nil) -> Promise<Void> {
|
||||
guard Storage.shared.getUser()?.name != nil, let configurationMessage = ConfigurationMessage.getCurrent(with: transaction) else {
|
||||
return Promise.value(())
|
||||
}
|
||||
|
||||
func forceSyncConfigurationNowIfNeeded() -> Promise<Void> {
|
||||
let destination = Message.Destination.contact(publicKey: getUserHexEncodedPublicKey())
|
||||
let (promise, seal) = Promise<Void>.pending()
|
||||
|
||||
// Note: SQLite only supports a single write thread so we can be sure this will retrieve the most up-to-date data
|
||||
Storage.writeSync { transaction in
|
||||
guard Storage.shared.getUser(using: transaction)?.name != nil, let configurationMessage = ConfigurationMessage.getCurrent(with: transaction) else {
|
||||
seal.fulfill(())
|
||||
return
|
||||
}
|
||||
|
||||
MessageSender.send(configurationMessage, to: destination, using: transaction).done {
|
||||
seal.fulfill(())
|
||||
}.catch { _ in
|
||||
|
|
|
@ -135,7 +135,7 @@ final class NukeDataModal : Modal {
|
|||
appDelegate.forceSyncConfigurationNowIfNeeded().ensure(on: DispatchQueue.main) {
|
||||
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
|
||||
UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later
|
||||
General.Cache.cachedEncodedPublicKey = nil // Remove the cached key so it gets re-cached on next access
|
||||
General.Cache.cachedEncodedPublicKey.mutate { $0 = nil } // Remove the cached key so it gets re-cached on next access
|
||||
NotificationCenter.default.post(name: .dataNukeRequested, object: nil)
|
||||
}.retainUntilComplete()
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ final class NukeDataModal : Modal {
|
|||
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
|
||||
let potentiallyMaliciousSnodes = confirmations.compactMap { $0.value == false ? $0.key : nil }
|
||||
if potentiallyMaliciousSnodes.isEmpty {
|
||||
General.Cache.cachedEncodedPublicKey = nil // Remove the cached key so it gets re-cached on next access
|
||||
General.Cache.cachedEncodedPublicKey.mutate { $0 = nil } // Remove the cached key so it gets re-cached on next access
|
||||
UserDefaults.removeAll() // Not done in the nuke data implementation as unlinking requires this to happen later
|
||||
NotificationCenter.default.post(name: .dataNukeRequested, object: nil)
|
||||
} else {
|
||||
|
|
|
@ -40,7 +40,7 @@ extension Storage {
|
|||
}
|
||||
|
||||
public func getUser(using transaction: YapDatabaseReadTransaction?) -> Contact? {
|
||||
guard let userPublicKey = getUserPublicKey() else { return nil }
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
var result: Contact?
|
||||
|
||||
if let transaction = transaction {
|
||||
|
|
|
@ -2,7 +2,7 @@ import SessionUtilitiesKit
|
|||
|
||||
extension ConfigurationMessage {
|
||||
|
||||
public static func getCurrent(with transaction: YapDatabaseReadWriteTransaction? = nil) -> ConfigurationMessage? {
|
||||
public static func getCurrent(with transaction: YapDatabaseReadTransaction) -> ConfigurationMessage? {
|
||||
let storage = Storage.shared
|
||||
guard let user = storage.getUser(using: transaction) else { return nil }
|
||||
|
||||
|
@ -13,94 +13,84 @@ extension ConfigurationMessage {
|
|||
var openGroups: Set<String> = []
|
||||
var contacts: Set<ConfigurationMessage.Contact> = []
|
||||
|
||||
let populateDataClosure: (YapDatabaseReadTransaction) -> () = { transaction in
|
||||
TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in
|
||||
guard let thread = object as? TSGroupThread else { return }
|
||||
|
||||
switch thread.groupModel.groupType {
|
||||
case .closedGroup:
|
||||
guard thread.isCurrentUserMemberInGroup() else { return }
|
||||
|
||||
let groupID = thread.groupModel.groupId
|
||||
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
|
||||
|
||||
guard
|
||||
storage.isClosedGroup(groupPublicKey, using: transaction),
|
||||
let encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey, using: transaction)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
let closedGroup = ClosedGroup(
|
||||
publicKey: groupPublicKey,
|
||||
name: (thread.groupModel.groupName ?? ""),
|
||||
encryptionKeyPair: encryptionKeyPair,
|
||||
members: Set(thread.groupModel.groupMemberIds),
|
||||
admins: Set(thread.groupModel.groupAdminIds),
|
||||
expirationTimer: thread.disappearingMessagesDuration(with: transaction)
|
||||
)
|
||||
closedGroups.insert(closedGroup)
|
||||
|
||||
case .openGroup:
|
||||
if let threadId: String = thread.uniqueId, let v2OpenGroup = storage.getV2OpenGroup(for: threadId) {
|
||||
openGroups.insert("\(v2OpenGroup.server)/\(v2OpenGroup.room)?public_key=\(v2OpenGroup.publicKey)")
|
||||
}
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
TSGroupThread.enumerateCollectionObjects(with: transaction) { object, _ in
|
||||
guard let thread = object as? TSGroupThread else { return }
|
||||
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey()
|
||||
|
||||
contacts = storage.getAllContacts(with: transaction)
|
||||
.filter { contact -> Bool in
|
||||
let threadID = TSContactThread.threadID(fromContactSessionID: contact.sessionID)
|
||||
switch thread.groupModel.groupType {
|
||||
case .closedGroup:
|
||||
guard thread.isCurrentUserMemberInGroup() else { return }
|
||||
|
||||
return (
|
||||
// Skip the current user
|
||||
contact.sessionID != currentUserPublicKey &&
|
||||
// Contacts which have visible threads
|
||||
TSContactThread.fetch(uniqueId: threadID, transaction: transaction)?.shouldBeVisible == true && (
|
||||
|
||||
// Include already approved contacts
|
||||
contact.isApproved ||
|
||||
contact.didApproveMe ||
|
||||
|
||||
// Sync blocked contacts
|
||||
SSKEnvironment.shared.blockingManager.isRecipientIdBlocked(contact.sessionID)
|
||||
)
|
||||
)
|
||||
}
|
||||
.map { contact -> ConfigurationMessage.Contact in
|
||||
// Can just default the 'hasX' values to true as they will be set to this
|
||||
// when converting to proto anyway
|
||||
let profilePictureURL = contact.profilePictureURL
|
||||
let profileKey = contact.profileEncryptionKey?.keyData
|
||||
let groupID = thread.groupModel.groupId
|
||||
let groupPublicKey = LKGroupUtilities.getDecodedGroupID(groupID)
|
||||
|
||||
return ConfigurationMessage.Contact(
|
||||
publicKey: contact.sessionID,
|
||||
displayName: (contact.name ?? contact.sessionID),
|
||||
profilePictureURL: profilePictureURL,
|
||||
profileKey: profileKey,
|
||||
hasIsApproved: true,
|
||||
isApproved: contact.isApproved,
|
||||
hasIsBlocked: true,
|
||||
isBlocked: SSKEnvironment.shared.blockingManager.isRecipientIdBlocked(contact.sessionID),
|
||||
hasDidApproveMe: true,
|
||||
didApproveMe: contact.didApproveMe
|
||||
guard
|
||||
storage.isClosedGroup(groupPublicKey, using: transaction),
|
||||
let encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(for: groupPublicKey, using: transaction)
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
let closedGroup = ClosedGroup(
|
||||
publicKey: groupPublicKey,
|
||||
name: (thread.groupModel.groupName ?? ""),
|
||||
encryptionKeyPair: encryptionKeyPair,
|
||||
members: Set(thread.groupModel.groupMemberIds),
|
||||
admins: Set(thread.groupModel.groupAdminIds),
|
||||
expirationTimer: thread.disappearingMessagesDuration(with: transaction)
|
||||
)
|
||||
}
|
||||
.asSet()
|
||||
closedGroups.insert(closedGroup)
|
||||
|
||||
case .openGroup:
|
||||
if let threadId: String = thread.uniqueId, let v2OpenGroup = storage.getV2OpenGroup(for: threadId) {
|
||||
openGroups.insert("\(v2OpenGroup.server)/\(v2OpenGroup.room)?public_key=\(v2OpenGroup.publicKey)")
|
||||
}
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
// If we are provided with a transaction then read the data based on the state of the database
|
||||
// from within the transaction rather than the state in disk
|
||||
if let transaction: YapDatabaseReadWriteTransaction = transaction {
|
||||
populateDataClosure(transaction)
|
||||
}
|
||||
else {
|
||||
Storage.read { transaction in populateDataClosure(transaction) }
|
||||
}
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey()
|
||||
|
||||
contacts = storage.getAllContacts(with: transaction)
|
||||
.compactMap { contact -> ConfigurationMessage.Contact? in
|
||||
let threadID = TSContactThread.threadID(fromContactSessionID: contact.sessionID)
|
||||
|
||||
guard
|
||||
// Skip the current user
|
||||
contact.sessionID != currentUserPublicKey &&
|
||||
// Contacts which have visible threads
|
||||
TSContactThread.fetch(uniqueId: threadID, transaction: transaction)?.shouldBeVisible == true && (
|
||||
|
||||
// Include already approved contacts
|
||||
contact.isApproved ||
|
||||
contact.didApproveMe ||
|
||||
|
||||
// Sync blocked contacts
|
||||
SSKEnvironment.shared.blockingManager.isRecipientIdBlocked(contact.sessionID)
|
||||
)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Can just default the 'hasX' values to true as they will be set to this
|
||||
// when converting to proto anyway
|
||||
let profilePictureURL = contact.profilePictureURL
|
||||
let profileKey = contact.profileEncryptionKey?.keyData
|
||||
|
||||
return ConfigurationMessage.Contact(
|
||||
publicKey: contact.sessionID,
|
||||
displayName: (contact.name ?? contact.sessionID),
|
||||
profilePictureURL: profilePictureURL,
|
||||
profileKey: profileKey,
|
||||
hasIsApproved: true,
|
||||
isApproved: contact.isApproved,
|
||||
hasIsBlocked: true,
|
||||
isBlocked: SSKEnvironment.shared.blockingManager.isRecipientIdBlocked(contact.sessionID),
|
||||
hasDidApproveMe: true,
|
||||
didApproveMe: contact.didApproveMe
|
||||
)
|
||||
}
|
||||
.asSet()
|
||||
|
||||
return ConfigurationMessage(
|
||||
displayName: displayName,
|
||||
|
|
|
@ -869,12 +869,14 @@ extension MessageReceiver {
|
|||
// a new configuration message (otherwise the `contact` will be loaded direct from the database and the
|
||||
// `didApproveMe` value won't have been updated)
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
guard Storage.shared.getUser()?.name != nil, let configurationMessage = ConfigurationMessage.getCurrent() else {
|
||||
return
|
||||
Storage.write { transaction in
|
||||
guard Storage.shared.getUser()?.name != nil, let configurationMessage = ConfigurationMessage.getCurrent(with: transaction) else {
|
||||
return
|
||||
}
|
||||
|
||||
let destination: Message.Destination = Message.Destination.contact(publicKey: userPublicKey)
|
||||
MessageSender.send(configurationMessage, to: destination, using: transaction).retainUntilComplete()
|
||||
}
|
||||
|
||||
let destination: Message.Destination = Message.Destination.contact(publicKey: userPublicKey)
|
||||
MessageSender.send(configurationMessage, to: destination, using: transaction).retainUntilComplete()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
|||
|
||||
public enum General {
|
||||
public enum Cache {
|
||||
public static var cachedEncodedPublicKey: String? = nil
|
||||
public static var cachedEncodedPublicKey: Atomic<String?> = Atomic(nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,10 @@ public class GeneralUtilities: NSObject {
|
|||
}
|
||||
|
||||
public func getUserHexEncodedPublicKey() -> String {
|
||||
if let cachedKey: String = General.Cache.cachedEncodedPublicKey { return cachedKey }
|
||||
if let cachedKey: String = General.Cache.cachedEncodedPublicKey.wrappedValue { return cachedKey }
|
||||
|
||||
if let keyPair = OWSIdentityManager.shared().identityKeyPair() { // Can be nil under some circumstances
|
||||
General.Cache.cachedEncodedPublicKey = keyPair.hexEncodedPublicKey
|
||||
General.Cache.cachedEncodedPublicKey.mutate { $0 = keyPair.hexEncodedPublicKey }
|
||||
return keyPair.hexEncodedPublicKey
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
import Foundation
|
||||
|
||||
// MARK: - Atomic<Value>
|
||||
/// The `Atomic<Value>` wrapper is a generic wrapper providing a thread-safe way to get and set a value
|
||||
///
|
||||
/// A write-up on the need for this class and it's approach can be found here:
|
||||
/// https://www.vadimbulavin.com/swift-atomic-properties-with-property-wrappers/
|
||||
/// there is also another approach which can be taken but it requires separate types for collections and results in
|
||||
/// a somewhat inconsistent interface between different `Atomic` wrappers
|
||||
@propertyWrapper
|
||||
public class Atomic<Value> {
|
||||
private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)")
|
||||
private var value: Value
|
||||
|
||||
/// In order to change the value you **must** use the `mutate` function
|
||||
public var wrappedValue: Value {
|
||||
return queue.sync { return value }
|
||||
}
|
||||
|
||||
/// For more information see https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#projections
|
||||
public var projectedValue: Atomic<Value> {
|
||||
return self
|
||||
}
|
||||
|
||||
// MARK: - Initialization
|
||||
public init(_ initialValue: Value) {
|
||||
self.value = initialValue
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
public func mutate(_ mutation: (inout Value) -> Void) {
|
||||
return queue.sync {
|
||||
mutation(&value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Atomic where Value: CustomDebugStringConvertible {
|
||||
var debugDescription: String {
|
||||
return value.debugDescription
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue