Started adding logic and unit tests for group creation
Reworked the config store to better support different types of config objects Added the logic to create a group (not final just yet)
This commit is contained in:
parent
f44b545265
commit
f1075e9123
|
@ -1 +1 @@
|
|||
Subproject commit 8d9ce6e30153a785b13354c99a9a210d5e8fc1a7
|
||||
Subproject commit 517a61a455d31cd9363198d1b3d02f20093a5811
|
File diff suppressed because it is too large
Load Diff
|
@ -283,13 +283,13 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
|
|||
|
||||
// MARK: - Call Message Handling
|
||||
|
||||
public func updateCallMessage(mode: EndCallMode) {
|
||||
public func updateCallMessage(mode: EndCallMode, using dependencies: Dependencies = Dependencies()) {
|
||||
guard let callInteractionId: Int64 = callInteractionId else { return }
|
||||
|
||||
let duration: TimeInterval = self.duration
|
||||
let hasStartedConnecting: Bool = self.hasStartedConnecting
|
||||
|
||||
Storage.shared.writeAsync(
|
||||
dependencies.storage.writeAsync(
|
||||
updates: { db in
|
||||
guard let interaction: Interaction = try? Interaction.fetchOne(db, id: callInteractionId) else {
|
||||
return
|
||||
|
@ -344,7 +344,8 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
|
|||
threadId: interaction.threadId,
|
||||
threadVariant: threadVariant,
|
||||
includingOlder: false,
|
||||
trySendReadReceipt: false
|
||||
trySendReadReceipt: false,
|
||||
using: dependencies
|
||||
)
|
||||
},
|
||||
completion: { _, _ in
|
||||
|
|
|
@ -128,7 +128,10 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
public func reportCurrentCallEnded(reason: CXCallEndedReason?) {
|
||||
public func reportCurrentCallEnded(
|
||||
reason: CXCallEndedReason?,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
guard Thread.isMainThread else {
|
||||
DispatchQueue.main.async {
|
||||
self.reportCurrentCallEnded(reason: reason)
|
||||
|
@ -155,14 +158,14 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
|
|||
self.provider?.reportCall(with: call.callId, endedAt: nil, reason: reason)
|
||||
|
||||
switch (reason) {
|
||||
case .answeredElsewhere: call.updateCallMessage(mode: .answeredElsewhere)
|
||||
case .unanswered: call.updateCallMessage(mode: .unanswered)
|
||||
case .declinedElsewhere: call.updateCallMessage(mode: .local)
|
||||
default: call.updateCallMessage(mode: .remote)
|
||||
case .answeredElsewhere: call.updateCallMessage(mode: .answeredElsewhere, using: dependencies)
|
||||
case .unanswered: call.updateCallMessage(mode: .unanswered, using: dependencies)
|
||||
case .declinedElsewhere: call.updateCallMessage(mode: .local, using: dependencies)
|
||||
default: call.updateCallMessage(mode: .remote, using: dependencies)
|
||||
}
|
||||
}
|
||||
else {
|
||||
call.updateCallMessage(mode: .local)
|
||||
call.updateCallMessage(mode: .local, using: dependencies)
|
||||
}
|
||||
|
||||
call.webRTCSession.dropConnection()
|
||||
|
|
|
@ -31,7 +31,7 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate
|
|||
private lazy var data: [ArraySection<Section, Profile>] = [
|
||||
ArraySection(model: .contacts, elements: contactProfiles)
|
||||
]
|
||||
private var selectedContacts: Set<String> = []
|
||||
private var selectedProfiles: [String: Profile] = [:]
|
||||
private var searchText: String = ""
|
||||
|
||||
// MARK: - Components
|
||||
|
@ -211,7 +211,7 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate
|
|||
leftAccessory: .profile(id: profile.id, profile: profile),
|
||||
title: profile.displayName(),
|
||||
rightAccessory: .radio(isSelected: { [weak self] in
|
||||
self?.selectedContacts.contains(profile.id) == true
|
||||
(self?.selectedProfiles[profile.id] != nil)
|
||||
}),
|
||||
styling: SessionCell.StyleInfo(backgroundStyle: .edgeToEdge),
|
||||
accessibility: Accessibility(
|
||||
|
@ -234,13 +234,13 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate
|
|||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let profileId: String = data[indexPath.section].elements[indexPath.row].id
|
||||
let profile: Profile = data[indexPath.section].elements[indexPath.row]
|
||||
|
||||
if !selectedContacts.contains(profileId) {
|
||||
selectedContacts.insert(profileId)
|
||||
if selectedProfiles[profile.id] == nil {
|
||||
selectedProfiles[profile.id] = profile
|
||||
}
|
||||
else {
|
||||
selectedContacts.remove(profileId)
|
||||
selectedProfiles.removeValue(forKey: profile.id)
|
||||
}
|
||||
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
|
@ -323,17 +323,17 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate
|
|||
guard name.utf8CString.count < SessionUtil.libSessionMaxGroupNameByteLength else {
|
||||
return showError(title: "vc_create_closed_group_group_name_too_long_error".localized())
|
||||
}
|
||||
guard selectedContacts.count >= 1 else {
|
||||
guard selectedProfiles.count >= 1 else {
|
||||
return showError(title: "GROUP_ERROR_NO_MEMBER_SELECTION".localized())
|
||||
}
|
||||
guard selectedContacts.count < 100 else { // Minus one because we're going to include self later
|
||||
guard selectedProfiles.count < 100 else { // Minus one because we're going to include self later
|
||||
return showError(title: "vc_create_closed_group_too_many_group_members_error".localized())
|
||||
}
|
||||
let selectedContacts = self.selectedContacts
|
||||
let message: String? = (selectedContacts.count > 20 ? "GROUP_CREATION_PLEASE_WAIT".localized() : nil)
|
||||
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, message: message) { [weak self] _ in
|
||||
let selectedProfiles: [(String, Profile?)] = self.selectedProfiles
|
||||
.reduce(into: []) { result, next in result.append((next.key, next.value)) }
|
||||
ModalActivityIndicatorViewController.present(fromViewController: navigationController!) { [weak self] _ in
|
||||
MessageSender
|
||||
.createClosedGroup(name: name, members: selectedContacts)
|
||||
.createLegacyClosedGroup(name: name, members: selectedProfiles.map { $0.0 }.asSet())
|
||||
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sinkUntilComplete(
|
||||
|
|
|
@ -530,7 +530,8 @@ class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel<ThreadD
|
|||
.update(
|
||||
db,
|
||||
sessionId: threadId,
|
||||
disappearingMessagesConfig: updatedConfig
|
||||
disappearingMessagesConfig: updatedConfig,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .legacyGroup:
|
||||
|
@ -538,7 +539,17 @@ class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel<ThreadD
|
|||
.update(
|
||||
db,
|
||||
legacyGroupPublicKey: threadId,
|
||||
disappearingConfig: updatedConfig
|
||||
disappearingConfig: updatedConfig,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .group:
|
||||
try SessionUtil
|
||||
.update(
|
||||
db,
|
||||
groupIdentityPublicKey: threadId,
|
||||
disappearingConfig: updatedConfig,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
default: break
|
||||
|
|
|
@ -531,14 +531,15 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
|
|||
confirmStyle: .danger,
|
||||
cancelStyle: .alert_text
|
||||
),
|
||||
onTap: { [weak self] in
|
||||
onTap: {
|
||||
dependencies.storage.write { db in
|
||||
try SessionThread.deleteOrLeave(
|
||||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
groupLeaveType: .standard,
|
||||
calledFromConfigHandling: false
|
||||
calledFromConfigHandling: false,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,12 +111,15 @@ public struct SessionApp {
|
|||
|
||||
// MARK: - Functions
|
||||
|
||||
public static func resetAppData(onReset: (() -> ())? = nil) {
|
||||
public static func resetAppData(
|
||||
onReset: (() -> ())? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
// This _should_ be wiped out below.
|
||||
Logger.error("")
|
||||
DDLog.flushLog()
|
||||
|
||||
SessionUtil.clearMemoryState()
|
||||
SessionUtil.clearMemoryState(using: dependencies)
|
||||
Storage.resetAllStorage()
|
||||
ProfileManager.resetProfileStorage()
|
||||
Attachment.resetAttachmentStorage()
|
||||
|
|
|
@ -568,7 +568,7 @@ class NotificationActionHandler {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
return Storage.shared
|
||||
return dependencies.storage
|
||||
.writePublisher { db in
|
||||
let interaction: Interaction = try Interaction(
|
||||
threadId: threadId,
|
||||
|
@ -589,7 +589,8 @@ class NotificationActionHandler {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: thread.variant
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
return try MessageSender.preparedSendData(
|
||||
|
@ -646,8 +647,11 @@ class NotificationActionHandler {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
private func markAsRead(threadId: String) -> AnyPublisher<Void, Error> {
|
||||
return Storage.shared
|
||||
private func markAsRead(
|
||||
threadId: String,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Void, Error> {
|
||||
return dependencies.storage
|
||||
.writePublisher { db in
|
||||
guard
|
||||
let threadVariant: SessionThread.Variant = try SessionThread
|
||||
|
@ -673,7 +677,8 @@ class NotificationActionHandler {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
|
|
|
@ -84,12 +84,12 @@ enum Onboarding {
|
|||
|
||||
/// If the user returns to an earlier screen during Onboarding we might need to clear out a partially created
|
||||
/// account (eg. returning from the PN setting screen to the seed entry screen when linking a device)
|
||||
func unregister() {
|
||||
func unregister(using dependencies: Dependencies = Dependencies()) {
|
||||
// Clear the in-memory state from SessionUtil
|
||||
SessionUtil.clearMemoryState()
|
||||
SessionUtil.clearMemoryState(using: dependencies)
|
||||
|
||||
// Clear any data which gets set during Onboarding
|
||||
Storage.shared.write { db in
|
||||
dependencies.storage.write { db in
|
||||
db[.hasViewedSeed] = false
|
||||
|
||||
try SessionThread.deleteAll(db)
|
||||
|
@ -104,19 +104,17 @@ enum Onboarding {
|
|||
profileNameRetrievalIdentifier.mutate { $0 = nil }
|
||||
profileNameRetrievalPublisher.mutate { $0 = nil }
|
||||
|
||||
UserDefaults.standard[.hasSyncedInitialConfiguration] = false
|
||||
dependencies.standardUserDefaults[.hasSyncedInitialConfiguration] = false
|
||||
}
|
||||
|
||||
func preregister(with seed: Data, ed25519KeyPair: KeyPair, x25519KeyPair: KeyPair) {
|
||||
func preregister(
|
||||
with seed: Data,
|
||||
ed25519KeyPair: KeyPair,
|
||||
x25519KeyPair: KeyPair,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
let x25519PublicKey = x25519KeyPair.hexEncodedPublicKey
|
||||
|
||||
// Create the initial shared util state (won't have been created on
|
||||
// launch due to lack of ed25519 key)
|
||||
SessionUtil.loadState(
|
||||
userPublicKey: x25519PublicKey,
|
||||
ed25519SecretKey: ed25519KeyPair.secretKey
|
||||
)
|
||||
|
||||
// Store the user identity information
|
||||
Storage.shared.write { db in
|
||||
try Identity.store(
|
||||
|
@ -126,6 +124,10 @@ enum Onboarding {
|
|||
x25519KeyPair: x25519KeyPair
|
||||
)
|
||||
|
||||
// Create the initial shared util state (won't have been created on
|
||||
// launch due to lack of ed25519 key)
|
||||
SessionUtil.loadState(db, using: dependencies)
|
||||
|
||||
// No need to show the seed again if the user is restoring or linking
|
||||
db[.hasViewedSeed] = (self == .recover || self == .link)
|
||||
|
||||
|
@ -140,7 +142,8 @@ enum Onboarding {
|
|||
db,
|
||||
Contact.Columns.isTrusted.set(to: true), // Always trust the current user
|
||||
Contact.Columns.isApproved.set(to: true),
|
||||
Contact.Columns.didApproveMe.set(to: true)
|
||||
Contact.Columns.didApproveMe.set(to: true),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
/// Create the 'Note to Self' thread (not visible by default)
|
||||
|
@ -154,7 +157,8 @@ enum Onboarding {
|
|||
.filter(id: x25519PublicKey)
|
||||
.updateAllAndConfig(
|
||||
db,
|
||||
SessionThread.Columns.shouldBeVisible.set(to: false)
|
||||
SessionThread.Columns.shouldBeVisible.set(to: false),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -162,28 +166,29 @@ enum Onboarding {
|
|||
// home screen a configuration sync is triggered (yes, the logic is a
|
||||
// bit weird). This is needed so that if the user registers and
|
||||
// immediately links a device, there'll be a configuration in their swarm.
|
||||
UserDefaults.standard[.hasSyncedInitialConfiguration] = (self == .register)
|
||||
dependencies.standardUserDefaults[.hasSyncedInitialConfiguration] = (self == .register)
|
||||
|
||||
// Only continue if this isn't a new account
|
||||
guard self != .register else { return }
|
||||
|
||||
// Fetch the
|
||||
Onboarding.profileNamePublisher
|
||||
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
|
||||
.subscribe(on: DispatchQueue.global(qos: .userInitiated), using: dependencies)
|
||||
.sinkUntilComplete()
|
||||
}
|
||||
|
||||
func completeRegistration() {
|
||||
func completeRegistration(using dependencies: Dependencies = Dependencies()) {
|
||||
// Set the `lastNameUpdate` to the current date, so that we don't overwrite
|
||||
// what the user set in the display name step with whatever we find in their
|
||||
// swarm (otherwise the user could enter a display name and have it immediately
|
||||
// overwritten due to the config request running slow)
|
||||
Storage.shared.write { db in
|
||||
dependencies.storage.write { db in
|
||||
try Profile
|
||||
.filter(id: getUserHexEncodedPublicKey(db))
|
||||
.updateAllAndConfig(
|
||||
db,
|
||||
Profile.Columns.lastNameUpdate.set(to: Date().timeIntervalSince1970)
|
||||
Profile.Columns.lastNameUpdate.set(to: Date().timeIntervalSince1970),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -192,7 +197,7 @@ enum Onboarding {
|
|||
|
||||
// Now that we have registered get the Snode pool (just in case) - other non-blocking
|
||||
// launch jobs will automatically be run because the app activation was triggered
|
||||
GetSnodePoolJob.run()
|
||||
GetSnodePoolJob.run(using: dependencies)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,12 +10,14 @@ import SessionMessagingKit
|
|||
import SessionUtilitiesKit
|
||||
|
||||
class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.NavButton, PrivacySettingsViewModel.Section, PrivacySettingsViewModel.Item> {
|
||||
private let dependencies: Dependencies
|
||||
private let shouldShowCloseButton: Bool
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
init(shouldShowCloseButton: Bool = false) {
|
||||
init(shouldShowCloseButton: Bool = false, using dependencies: Dependencies = Dependencies()) {
|
||||
self.shouldShowCloseButton = shouldShowCloseButton
|
||||
self.dependencies = dependencies
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
@ -111,9 +113,9 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
|
|||
}
|
||||
.removeDuplicates()
|
||||
.handleEvents(didFail: { SNLog("[PrivacySettingsViewModel] Observation failed with error: \($0)") })
|
||||
.publisher(in: Storage.shared)
|
||||
.publisher(in: dependencies.storage)
|
||||
.withPrevious()
|
||||
.map { (previous: State?, current: State) -> [SectionModel] in
|
||||
.map { [dependencies] (previous: State?, current: State) -> [SectionModel] in
|
||||
return [
|
||||
SectionModel(
|
||||
model: .screenSecurity,
|
||||
|
@ -146,8 +148,12 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
|
|||
return
|
||||
}
|
||||
|
||||
Storage.shared.write { db in
|
||||
try db.setAndUpdateConfig(.isScreenLockEnabled, to: !db[.isScreenLockEnabled])
|
||||
dependencies.storage.write { db in
|
||||
try db.setAndUpdateConfig(
|
||||
.isScreenLockEnabled,
|
||||
to: !db[.isScreenLockEnabled],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -168,10 +174,11 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
|
|||
)
|
||||
),
|
||||
onTap: { [weak self] in
|
||||
Storage.shared.write { db in
|
||||
dependencies.storage.write { db in
|
||||
try db.setAndUpdateConfig(
|
||||
.checkForCommunityMessageRequests,
|
||||
to: !db[.checkForCommunityMessageRequests]
|
||||
to: !db[.checkForCommunityMessageRequests],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -193,8 +200,12 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
|
|||
)
|
||||
),
|
||||
onTap: {
|
||||
Storage.shared.write { db in
|
||||
try db.setAndUpdateConfig(.areReadReceiptsEnabled, to: !db[.areReadReceiptsEnabled])
|
||||
dependencies.storage.write { db in
|
||||
try db.setAndUpdateConfig(
|
||||
.areReadReceiptsEnabled,
|
||||
to: !db[.areReadReceiptsEnabled],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -247,8 +258,12 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
|
|||
)
|
||||
),
|
||||
onTap: {
|
||||
Storage.shared.write { db in
|
||||
try db.setAndUpdateConfig(.typingIndicatorsEnabled, to: !db[.typingIndicatorsEnabled])
|
||||
dependencies.storage.write { db in
|
||||
try db.setAndUpdateConfig(
|
||||
.typingIndicatorsEnabled,
|
||||
to: !db[.typingIndicatorsEnabled],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -269,8 +284,12 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
|
|||
)
|
||||
),
|
||||
onTap: {
|
||||
Storage.shared.write { db in
|
||||
try db.setAndUpdateConfig(.areLinkPreviewsEnabled, to: !db[.areLinkPreviewsEnabled])
|
||||
dependencies.storage.write { db in
|
||||
try db.setAndUpdateConfig(
|
||||
.areLinkPreviewsEnabled,
|
||||
to: !db[.areLinkPreviewsEnabled],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -303,8 +322,12 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
|
|||
onConfirm: { _ in Permissions.requestMicrophonePermissionIfNeeded() }
|
||||
),
|
||||
onTap: {
|
||||
Storage.shared.write { db in
|
||||
try db.setAndUpdateConfig(.areCallsEnabled, to: !db[.areCallsEnabled])
|
||||
dependencies.storage.write { db in
|
||||
try db.setAndUpdateConfig(
|
||||
.areCallsEnabled,
|
||||
to: !db[.areCallsEnabled],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
@ -200,7 +200,8 @@ enum MockDataGenerator {
|
|||
_ = try! ClosedGroup(
|
||||
threadId: randomGroupPublicKey,
|
||||
name: groupName,
|
||||
formationTimestamp: timestampNow
|
||||
formationTimestamp: timestampNow,
|
||||
approved: true
|
||||
)
|
||||
.saved(db)
|
||||
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
|
||||
import Foundation
|
||||
import CallKit
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public protocol CallManagerProtocol {
|
||||
var currentCall: CurrentCallProtocol? { get set }
|
||||
|
||||
func reportCurrentCallEnded(reason: CXCallEndedReason?)
|
||||
func reportCurrentCallEnded(reason: CXCallEndedReason?, using dependencies: Dependencies)
|
||||
|
||||
func showCallUIForCall(caller: String, uuid: String, mode: CallMode, interactionId: Int64?)
|
||||
func handleAnswerMessage(_ message: CallMessage)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import Foundation
|
||||
import GRDB
|
||||
import WebRTC
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public protocol CurrentCallProtocol {
|
||||
var uuid: String { get }
|
||||
|
@ -11,7 +12,7 @@ public protocol CurrentCallProtocol {
|
|||
var hasStartedConnecting: Bool { get set }
|
||||
var hasEnded: Bool { get set }
|
||||
|
||||
func updateCallMessage(mode: EndCallMode)
|
||||
func updateCallMessage(mode: EndCallMode, using dependencies: Dependencies)
|
||||
func didReceiveRemoteSDP(sdp: RTCSessionDescription)
|
||||
func startSessionCall(_ db: Database)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,8 @@ public enum SNMessagingKit: MigratableTarget { // Just to make the external API
|
|||
_013_SessionUtilChanges.self,
|
||||
_014_GenerateInitialUserConfigDumps.self,
|
||||
_015_BlockCommunityMessageRequests.self,
|
||||
_016_DisappearingMessagesConfiguration.self
|
||||
_016_DisappearingMessagesConfiguration.self,
|
||||
_017_GroupsRebuildChanges.self
|
||||
]
|
||||
]
|
||||
)
|
||||
|
|
|
@ -16,7 +16,7 @@ enum _001_InitialSetupMigration: Migration {
|
|||
return .porter(wrapping: .unicode61())
|
||||
}()
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.create(table: Contact.self) { t in
|
||||
t.column(.id, .text)
|
||||
.notNull()
|
||||
|
|
|
@ -13,7 +13,7 @@ enum _002_SetupStandardJobs: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
// Start by adding the jobs that don't have collections (in the jobs like these
|
||||
// will be added via migrations)
|
||||
try autoreleasepool {
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
static let needsConfigSync: Bool = true
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
guard !SNUtilitiesKit.isRunningTests else { return Storage.update(progress: 1, for: self, in: target) }
|
||||
|
||||
SNLogNotTests("[Migration Error] Attempted to perform legacy migation")
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _004_RemoveLegacyYDB: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ enum _005_FixDeletedMessageReadState: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.01
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
_ = try Interaction
|
||||
.filter(
|
||||
Interaction.Columns.variant == Interaction.Variant.standardIncomingDeleted ||
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _006_FixHiddenModAdminSupport: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.01
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.alter(table: GroupMember.self) { t in
|
||||
t.add(.isHidden, .boolean)
|
||||
.notNull()
|
||||
|
|
|
@ -11,7 +11,7 @@ enum _007_HomeQueryOptimisationIndexes: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.01
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.create(
|
||||
index: "interaction_on_wasRead_and_hasMention_and_threadId",
|
||||
on: Interaction.databaseTableName,
|
||||
|
|
|
@ -11,7 +11,7 @@ enum _008_EmojiReacts: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.01
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.create(table: Reaction.self) { t in
|
||||
t.column(.interactionId, .numeric)
|
||||
.notNull()
|
||||
|
|
|
@ -10,7 +10,7 @@ enum _009_OpenGroupPermission: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.01
|
||||
|
||||
static func migrate(_ db: GRDB.Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.alter(table: OpenGroup.self) { t in
|
||||
t.add(.permissions, .integer)
|
||||
.defaults(to: OpenGroup.Permissions.all)
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _010_AddThreadIdToFTS: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 3
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
// Can't actually alter a virtual table in SQLite so we need to drop and recreate it,
|
||||
// luckily this is actually pretty quick
|
||||
if try db.tableExists(Interaction.fullTextSearchTableName) {
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _011_AddPendingReadReceipts: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.01
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.create(table: PendingReadReceipt.self) { t in
|
||||
t.column(.threadId, .text)
|
||||
.notNull()
|
||||
|
|
|
@ -11,7 +11,7 @@ enum _012_AddFTSIfNeeded: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.01
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
// Fix an issue that the fullTextSearchTable was dropped unintentionally and global search won't work.
|
||||
// This issue only happens to internal test users.
|
||||
if try db.tableExists(Interaction.fullTextSearchTableName) == false {
|
||||
|
|
|
@ -13,7 +13,7 @@ enum _013_SessionUtilChanges: Migration {
|
|||
static let needsConfigSync: Bool = true
|
||||
static let minExpectedRunDuration: TimeInterval = 0.4
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
// Add `markedAsUnread` to the thread table
|
||||
try db.alter(table: SessionThread.self) { t in
|
||||
t.add(.markedAsUnread, .boolean)
|
||||
|
@ -187,7 +187,7 @@ enum _013_SessionUtilChanges: Migration {
|
|||
)
|
||||
|
||||
// If we don't have an ed25519 key then no need to create cached dump data
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
|
||||
/// Remove any hidden threads to avoid syncing them (they are basically shadow threads created by starting a conversation
|
||||
/// but not sending a message so can just be cleared out)
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
static let needsConfigSync: Bool = true
|
||||
static let minExpectedRunDuration: TimeInterval = 4.0
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
// If we have no ed25519 key then there is no need to create cached dump data
|
||||
guard Identity.fetchUserEd25519KeyPair(db) != nil else {
|
||||
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
|
||||
|
@ -23,7 +23,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let timestampMs: Int64 = Int64(Date().timeIntervalSince1970 * 1000)
|
||||
|
||||
SessionUtil.loadState(db)
|
||||
SessionUtil.loadState(db, using: dependencies)
|
||||
|
||||
// Retrieve all threads (we are going to base the config dump data on the active
|
||||
// threads rather than anything else in the database)
|
||||
|
@ -33,12 +33,12 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
|
||||
// MARK: - UserProfile Config Dump
|
||||
|
||||
try SessionUtil
|
||||
try dependencies.caches[.sessionUtil]
|
||||
.config(for: .userProfile, publicKey: userPublicKey)
|
||||
.mutate { conf in
|
||||
.mutate { config in
|
||||
try SessionUtil.update(
|
||||
profile: Profile.fetchOrCreateCurrentUser(db),
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
|
||||
try SessionUtil.updateNoteToSelf(
|
||||
|
@ -47,13 +47,13 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
|
||||
return Int32(allThreads[userPublicKey]?.pinnedPriority ?? 0)
|
||||
}(),
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
|
||||
if config_needs_dump(conf) {
|
||||
if config.needsDump {
|
||||
try SessionUtil
|
||||
.createDump(
|
||||
conf: conf,
|
||||
config: config,
|
||||
for: .userProfile,
|
||||
publicKey: userPublicKey,
|
||||
timestampMs: timestampMs
|
||||
|
@ -64,9 +64,9 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
|
||||
// MARK: - Contact Config Dump
|
||||
|
||||
try SessionUtil
|
||||
try dependencies.caches[.sessionUtil]
|
||||
.config(for: .contacts, publicKey: userPublicKey)
|
||||
.mutate { conf in
|
||||
.mutate { config in
|
||||
// Exclude Note to Self, community, group and outgoing blinded message requests
|
||||
let validContactIds: [String] = allThreads
|
||||
.values
|
||||
|
@ -113,13 +113,13 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
created: allThreads[data.contact.id]?.creationDateTimestamp
|
||||
)
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
|
||||
if config_needs_dump(conf) {
|
||||
if config.needsDump {
|
||||
try SessionUtil
|
||||
.createDump(
|
||||
conf: conf,
|
||||
config: config,
|
||||
for: .contacts,
|
||||
publicKey: userPublicKey,
|
||||
timestampMs: timestampMs
|
||||
|
@ -130,21 +130,21 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
|
||||
// MARK: - ConvoInfoVolatile Config Dump
|
||||
|
||||
try SessionUtil
|
||||
try dependencies.caches[.sessionUtil]
|
||||
.config(for: .convoInfoVolatile, publicKey: userPublicKey)
|
||||
.mutate { conf in
|
||||
.mutate { config in
|
||||
let volatileThreadInfo: [SessionUtil.VolatileThreadInfo] = SessionUtil.VolatileThreadInfo
|
||||
.fetchAll(db, ids: Array(allThreads.keys))
|
||||
|
||||
try SessionUtil.upsert(
|
||||
convoInfoVolatileChanges: volatileThreadInfo,
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
|
||||
if config_needs_dump(conf) {
|
||||
if config.needsDump {
|
||||
try SessionUtil
|
||||
.createDump(
|
||||
conf: conf,
|
||||
config: config,
|
||||
for: .convoInfoVolatile,
|
||||
publicKey: userPublicKey,
|
||||
timestampMs: timestampMs
|
||||
|
@ -155,16 +155,16 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
|
||||
// MARK: - UserGroups Config Dump
|
||||
|
||||
try SessionUtil
|
||||
try dependencies.caches[.sessionUtil]
|
||||
.config(for: .userGroups, publicKey: userPublicKey)
|
||||
.mutate { conf in
|
||||
.mutate { config in
|
||||
let legacyGroupData: [SessionUtil.LegacyGroupInfo] = try SessionUtil.LegacyGroupInfo.fetchAll(db)
|
||||
let communityData: [SessionUtil.OpenGroupUrlInfo] = try SessionUtil.OpenGroupUrlInfo
|
||||
.fetchAll(db, ids: Array(allThreads.keys))
|
||||
|
||||
try SessionUtil.upsert(
|
||||
legacyGroups: legacyGroupData,
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
try SessionUtil.upsert(
|
||||
communities: communityData
|
||||
|
@ -174,13 +174,13 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
priority: Int32(allThreads[urlInfo.threadId]?.pinnedPriority ?? 0)
|
||||
)
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
|
||||
if config_needs_dump(conf) {
|
||||
if config.needsDump {
|
||||
try SessionUtil
|
||||
.createDump(
|
||||
conf: conf,
|
||||
config: config,
|
||||
for: .userGroups,
|
||||
publicKey: userPublicKey,
|
||||
timestampMs: timestampMs
|
||||
|
@ -191,7 +191,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
|
|||
|
||||
// MARK: - Threads
|
||||
|
||||
try SessionUtil.updatingThreads(db, Array(allThreads.values))
|
||||
try SessionUtil.updatingThreads(db, Array(allThreads.values), using: dependencies)
|
||||
|
||||
// MARK: - Syncing
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _015_BlockCommunityMessageRequests: Migration {
|
|||
static let minExpectedRunDuration: TimeInterval = 0.01
|
||||
static var requirements: [MigrationRequirement] = [.sessionUtilStateLoaded]
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
// Add the new 'Profile' properties
|
||||
try db.alter(table: Profile.self) { t in
|
||||
t.add(.blocksCommunityMessageRequests, .boolean)
|
||||
|
@ -26,10 +26,10 @@ enum _015_BlockCommunityMessageRequests: Migration {
|
|||
Identity.userExists(db),
|
||||
(try Setting.exists(db, id: Setting.BoolKey.checkForCommunityMessageRequests.rawValue)) == false
|
||||
{
|
||||
let rawBlindedMessageRequestValue: Int32 = try SessionUtil
|
||||
let rawBlindedMessageRequestValue: Int32 = try dependencies.caches[.sessionUtil]
|
||||
.config(for: .userProfile, publicKey: getUserHexEncodedPublicKey(db))
|
||||
.wrappedValue
|
||||
.map { conf -> Int32 in try SessionUtil.rawBlindedMessageRequestValue(in: conf) }
|
||||
.map { config -> Int32 in try SessionUtil.rawBlindedMessageRequestValue(in: config) }
|
||||
.defaulting(to: -1)
|
||||
|
||||
// Use the value in the config if we happen to have one, otherwise use the default
|
||||
|
|
|
@ -11,7 +11,7 @@ enum _016_DisappearingMessagesConfiguration: Migration {
|
|||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static var requirements: [MigrationRequirement] = [.sessionUtilStateLoaded]
|
||||
|
||||
static func migrate(_ db: GRDB.Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.alter(table: DisappearingMessagesConfiguration.self) { t in
|
||||
t.add(.type, .integer)
|
||||
t.add(.lastChangeTimestampMs, .integer)
|
||||
|
@ -75,8 +75,8 @@ enum _016_DisappearingMessagesConfiguration: Migration {
|
|||
}
|
||||
|
||||
// Update the configs so the settings are synced
|
||||
_ = try SessionUtil.updatingDisappearingConfigsOneToOne(db, contactUpdate)
|
||||
_ = try SessionUtil.batchUpdate(db, disappearingConfigs: legacyGroupUpdate)
|
||||
_ = try SessionUtil.updatingDisappearingConfigsOneToOne(db, contactUpdate, using: dependencies)
|
||||
_ = try SessionUtil.batchUpdate(db, disappearingConfigs: legacyGroupUpdate, using: dependencies)
|
||||
|
||||
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ enum _017_GroupsRebuildChanges: Migration {
|
|||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
static var requirements: [MigrationRequirement] = [.sessionUtilStateLoaded]
|
||||
|
||||
static func migrate(_ db: GRDB.Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.alter(table: ClosedGroup.self) { t in
|
||||
t.add(.displayPictureUrl, .text)
|
||||
t.add(.displayPictureFilename, .text)
|
||||
|
|
|
@ -259,14 +259,16 @@ public extension ClosedGroup {
|
|||
db,
|
||||
legacyGroupIds: threadVariants
|
||||
.filter { $0.variant == .legacyGroup }
|
||||
.map { $0.id }
|
||||
.map { $0.id },
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
try SessionUtil.remove(
|
||||
db,
|
||||
groupIds: threadVariants
|
||||
.filter { $0.variant == .group }
|
||||
.map { $0.id }
|
||||
.map { $0.id },
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -520,7 +520,8 @@ public extension Interaction {
|
|||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
includingOlder: Bool,
|
||||
trySendReadReceipt: Bool
|
||||
trySendReadReceipt: Bool,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard let interactionId: Int64 = interactionId else { return }
|
||||
|
||||
|
@ -537,7 +538,8 @@ public extension Interaction {
|
|||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
interactionInfo: [InteractionReadInfo],
|
||||
lastReadTimestampMs: Int64
|
||||
lastReadTimestampMs: Int64,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
// Add the 'DisappearingMessagesJob' if needed - this will update any expiring
|
||||
// messages `expiresStartedAtMs` values in local database, and create seperate
|
||||
|
@ -549,7 +551,8 @@ public extension Interaction {
|
|||
interactionIds: interactionInfo.map { $0.id },
|
||||
startedAtMs: TimeInterval(SnodeAPI.currentOffsetTimestampMs()),
|
||||
threadId: threadId
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Update the last read timestamp if needed
|
||||
|
@ -557,7 +560,8 @@ public extension Interaction {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
lastReadTimestampMs: lastReadTimestampMs
|
||||
lastReadTimestampMs: lastReadTimestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Clear out any notifications for the interactions we mark as read
|
||||
|
@ -588,7 +592,8 @@ public extension Interaction {
|
|||
interactionIds: interactionInfo
|
||||
.filter { !$0.wasRead && $0.variant != .standardOutgoing }
|
||||
.map { $0.id }
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -631,7 +636,8 @@ public extension Interaction {
|
|||
wasRead: false
|
||||
)
|
||||
],
|
||||
lastReadTimestampMs: timestampMs
|
||||
lastReadTimestampMs: timestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -654,7 +660,8 @@ public extension Interaction {
|
|||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
interactionInfo: [interactionInfo],
|
||||
lastReadTimestampMs: interactionInfo.timestampMs
|
||||
lastReadTimestampMs: interactionInfo.timestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -668,7 +675,8 @@ public extension Interaction {
|
|||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
interactionInfo: interactionInfoToMarkAsRead,
|
||||
lastReadTimestampMs: interactionInfo.timestampMs
|
||||
lastReadTimestampMs: interactionInfo.timestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -271,12 +271,15 @@ public extension Profile {
|
|||
///
|
||||
/// **Note:** This method intentionally does **not** save the newly created Profile,
|
||||
/// it will need to be explicitly saved after calling
|
||||
static func fetchOrCreateCurrentUser(_ db: Database? = nil, using dependencies: Dependencies = Dependencies()) -> Profile {
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
static func fetchOrCreateCurrentUser(
|
||||
_ db: Database? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Profile {
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
|
||||
guard let db: Database = db else {
|
||||
return dependencies.storage
|
||||
.read { db in fetchOrCreateCurrentUser(db) }
|
||||
.read { db in fetchOrCreateCurrentUser(db, using: dependencies) }
|
||||
.defaulting(to: defaultFor(userPublicKey))
|
||||
}
|
||||
|
||||
|
|
|
@ -115,7 +115,7 @@ public struct SessionThread: Codable, Identifiable, Equatable, FetchableRecord,
|
|||
public init(
|
||||
id: String,
|
||||
variant: Variant,
|
||||
creationDateTimestamp: TimeInterval = (TimeInterval(SnodeAPI.currentOffsetTimestampMs()) / 1000),
|
||||
creationDateTimestamp: TimeInterval? = nil,
|
||||
shouldBeVisible: Bool = false,
|
||||
isPinned: Bool = false,
|
||||
messageDraft: String? = nil,
|
||||
|
@ -123,11 +123,15 @@ public struct SessionThread: Codable, Identifiable, Equatable, FetchableRecord,
|
|||
mutedUntilTimestamp: TimeInterval? = nil,
|
||||
onlyNotifyForMentions: Bool = false,
|
||||
markedAsUnread: Bool? = false,
|
||||
pinnedPriority: Int32? = nil
|
||||
pinnedPriority: Int32? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
self.id = id
|
||||
self.variant = variant
|
||||
self.creationDateTimestamp = creationDateTimestamp
|
||||
self.creationDateTimestamp = (
|
||||
creationDateTimestamp ??
|
||||
(TimeInterval(SnodeAPI.currentOffsetTimestampMs(using: dependencies)) / 1000)
|
||||
)
|
||||
self.shouldBeVisible = shouldBeVisible
|
||||
self.messageDraft = messageDraft
|
||||
self.notificationSound = notificationSound
|
||||
|
@ -158,13 +162,15 @@ public extension SessionThread {
|
|||
_ db: Database,
|
||||
id: ID,
|
||||
variant: Variant,
|
||||
shouldBeVisible: Bool?
|
||||
shouldBeVisible: Bool?,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> SessionThread {
|
||||
guard let existingThread: SessionThread = try? fetchOne(db, id: id) else {
|
||||
return try SessionThread(
|
||||
id: id,
|
||||
variant: variant,
|
||||
shouldBeVisible: (shouldBeVisible ?? false)
|
||||
shouldBeVisible: (shouldBeVisible ?? false),
|
||||
using: dependencies
|
||||
).saved(db)
|
||||
}
|
||||
|
||||
|
@ -179,7 +185,8 @@ public extension SessionThread {
|
|||
.filter(id: id)
|
||||
.updateAllAndConfig(
|
||||
db,
|
||||
SessionThread.Columns.shouldBeVisible.set(to: shouldBeVisible)
|
||||
SessionThread.Columns.shouldBeVisible.set(to: shouldBeVisible),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Retrieve the updated thread and return it (we don't recursively call this method
|
||||
|
@ -187,8 +194,12 @@ public extension SessionThread {
|
|||
// would result in an infinite loop)
|
||||
return (try fetchOne(db, id: id))
|
||||
.defaulting(
|
||||
to: try SessionThread(id: id, variant: variant, shouldBeVisible: desiredVisibility)
|
||||
.saved(db)
|
||||
to: try SessionThread(
|
||||
id: id,
|
||||
variant: variant,
|
||||
shouldBeVisible: desiredVisibility,
|
||||
using: dependencies
|
||||
).saved(db)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -277,14 +288,16 @@ public extension SessionThread {
|
|||
threadId: String,
|
||||
threadVariant: Variant,
|
||||
groupLeaveType: ClosedGroup.LeaveType,
|
||||
calledFromConfigHandling: Bool
|
||||
calledFromConfigHandling: Bool,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws {
|
||||
try deleteOrLeave(
|
||||
db,
|
||||
threadIds: [threadId],
|
||||
threadVariant: threadVariant,
|
||||
groupLeaveType: groupLeaveType,
|
||||
calledFromConfigHandling: calledFromConfigHandling
|
||||
calledFromConfigHandling: calledFromConfigHandling,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -293,9 +306,10 @@ public extension SessionThread {
|
|||
threadIds: [String],
|
||||
threadVariant: Variant,
|
||||
groupLeaveType: ClosedGroup.LeaveType,
|
||||
calledFromConfigHandling: Bool
|
||||
calledFromConfigHandling: Bool,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws {
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let remainingThreadIds: [String] = threadIds.filter { $0 != currentUserPublicKey }
|
||||
|
||||
switch (threadVariant, groupLeaveType) {
|
||||
|
@ -312,7 +326,8 @@ public extension SessionThread {
|
|||
.updateAllAndConfig(
|
||||
db,
|
||||
SessionThread.Columns.pinnedPriority.set(to: 0),
|
||||
SessionThread.Columns.shouldBeVisible.set(to: false)
|
||||
SessionThread.Columns.shouldBeVisible.set(to: false),
|
||||
using: dependencies
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -320,7 +335,7 @@ public extension SessionThread {
|
|||
// If this wasn't called from config handling then we need to hide the conversation
|
||||
if !calledFromConfigHandling {
|
||||
try SessionUtil
|
||||
.hide(db, contactIds: threadIds)
|
||||
.hide(db, contactIds: threadIds, using: dependencies)
|
||||
}
|
||||
|
||||
_ = try SessionThread
|
||||
|
@ -333,7 +348,8 @@ public extension SessionThread {
|
|||
.leave(
|
||||
db,
|
||||
groupPublicKey: threadId,
|
||||
deleteThread: true
|
||||
deleteThread: true,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -342,7 +358,8 @@ public extension SessionThread {
|
|||
db,
|
||||
threadIds: threadIds,
|
||||
removeGroupData: true,
|
||||
calledFromConfigHandling: calledFromConfigHandling
|
||||
calledFromConfigHandling: calledFromConfigHandling,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case (.community, _):
|
||||
|
@ -350,7 +367,8 @@ public extension SessionThread {
|
|||
OpenGroupManager.shared.delete(
|
||||
db,
|
||||
openGroupId: threadId,
|
||||
calledFromConfigHandling: calledFromConfigHandling
|
||||
calledFromConfigHandling: calledFromConfigHandling,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,8 @@ public enum ConfigMessageReceiveJob: JobExecutor {
|
|||
try SessionUtil.handleConfigMessages(
|
||||
db,
|
||||
messages: sharedConfigMessages,
|
||||
publicKey: (job.threadId ?? "")
|
||||
publicKey: (job.threadId ?? ""),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
catch { lastError = error }
|
||||
|
|
|
@ -21,7 +21,9 @@ public enum ConfigurationSyncJob: JobExecutor {
|
|||
deferred: @escaping (Job, Dependencies) -> (),
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
guard Identity.userCompletedRequiredOnboarding() else { return success(job, true, dependencies) }
|
||||
guard Identity.userCompletedRequiredOnboarding(using: dependencies) else {
|
||||
return success(job, true, dependencies)
|
||||
}
|
||||
|
||||
// It's possible for multiple ConfigSyncJob's with the same target (user/group) to try to run at the
|
||||
// same time since as soon as one is started we will enqueue a second one, rather than adding dependencies
|
||||
|
@ -55,7 +57,9 @@ public enum ConfigurationSyncJob: JobExecutor {
|
|||
guard
|
||||
let publicKey: String = job.threadId,
|
||||
let pendingConfigChanges: [SessionUtil.OutgoingConfResult] = dependencies.storage
|
||||
.read(using: dependencies, { db in try SessionUtil.pendingChanges(db, publicKey: publicKey) })
|
||||
.read(using: dependencies, { db in
|
||||
try SessionUtil.pendingChanges(db, publicKey: publicKey, using: dependencies)
|
||||
})
|
||||
else {
|
||||
SNLog("[ConfigurationSyncJob] For \(job.threadId ?? "UnknownId") failed due to invalid data")
|
||||
return failure(job, StorageError.generic, false, dependencies)
|
||||
|
@ -129,7 +133,8 @@ public enum ConfigurationSyncJob: JobExecutor {
|
|||
return SessionUtil.markingAsPushed(
|
||||
message: change.message,
|
||||
serverHash: sendMessageResponse.hash,
|
||||
publicKey: publicKey
|
||||
publicKey: publicKey,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -219,7 +219,8 @@ public final class OpenGroupManager {
|
|||
.updateAllAndConfig(
|
||||
db,
|
||||
OpenGroup.Columns.isActive.set(to: true),
|
||||
OpenGroup.Columns.sequenceNumber.set(to: 0)
|
||||
OpenGroup.Columns.sequenceNumber.set(to: 0),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -269,7 +270,8 @@ public final class OpenGroupManager {
|
|||
db,
|
||||
server: server,
|
||||
rootToken: roomToken,
|
||||
publicKey: publicKey
|
||||
publicKey: publicKey,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -369,7 +371,7 @@ public final class OpenGroupManager {
|
|||
.deleteAll(db)
|
||||
|
||||
if !calledFromConfigHandling, let server: String = server, let roomToken: String = roomToken {
|
||||
try? SessionUtil.remove(db, server: server, roomToken: roomToken)
|
||||
try? SessionUtil.remove(db, server: server, roomToken: roomToken, using: dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: CallMessage
|
||||
message: CallMessage,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws {
|
||||
// Only support calls from contact threads
|
||||
guard threadVariant == .contact else { return }
|
||||
|
@ -19,7 +20,7 @@ extension MessageReceiver {
|
|||
switch message.kind {
|
||||
case .preOffer: try MessageReceiver.handleNewCallMessage(db, message: message)
|
||||
case .offer: MessageReceiver.handleOfferCallMessage(db, message: message)
|
||||
case .answer: MessageReceiver.handleAnswerCallMessage(db, message: message)
|
||||
case .answer: MessageReceiver.handleAnswerCallMessage(db, message: message, using: dependencies)
|
||||
case .provisionalAnswer: break // TODO: Implement
|
||||
|
||||
case let .iceCandidates(sdpMLineIndexes, sdpMids):
|
||||
|
@ -37,7 +38,7 @@ extension MessageReceiver {
|
|||
}
|
||||
currentWebRTCSession.handleICECandidates(candidates)
|
||||
|
||||
case .endCall: MessageReceiver.handleEndCallMessage(db, message: message)
|
||||
case .endCall: MessageReceiver.handleEndCallMessage(db, message: message, using: dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,7 +146,11 @@ extension MessageReceiver {
|
|||
currentCall.didReceiveRemoteSDP(sdp: sdpDescription)
|
||||
}
|
||||
|
||||
private static func handleAnswerCallMessage(_ db: Database, message: CallMessage) {
|
||||
private static func handleAnswerCallMessage(
|
||||
_ db: Database,
|
||||
message: CallMessage,
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
SNLog("[Calls] Received answer message.")
|
||||
|
||||
guard
|
||||
|
@ -161,7 +166,7 @@ extension MessageReceiver {
|
|||
guard !currentCall.hasStartedConnecting else { return }
|
||||
|
||||
callManager.dismissAllCallUI()
|
||||
callManager.reportCurrentCallEnded(reason: .answeredElsewhere)
|
||||
callManager.reportCurrentCallEnded(reason: .answeredElsewhere, using: dependencies)
|
||||
return
|
||||
}
|
||||
guard let sdp: String = message.sdps.first else { return }
|
||||
|
@ -172,7 +177,11 @@ extension MessageReceiver {
|
|||
callManager.handleAnswerMessage(message)
|
||||
}
|
||||
|
||||
private static func handleEndCallMessage(_ db: Database, message: CallMessage) {
|
||||
private static func handleEndCallMessage(
|
||||
_ db: Database,
|
||||
message: CallMessage,
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
SNLog("[Calls] Received end call message.")
|
||||
|
||||
guard
|
||||
|
@ -188,7 +197,8 @@ extension MessageReceiver {
|
|||
reason: (sender == getUserHexEncodedPublicKey(db) ?
|
||||
.declinedElsewhere :
|
||||
.remoteEnded
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -227,7 +237,8 @@ extension MessageReceiver {
|
|||
threadVariant: thread.variant,
|
||||
timestampMs: (messageSentTimestamp * 1000),
|
||||
userPublicKey: getUserHexEncodedPublicKey(db),
|
||||
openGroup: nil
|
||||
openGroup: nil,
|
||||
using: dependencies
|
||||
)
|
||||
)
|
||||
.inserted(db)
|
||||
|
@ -258,7 +269,8 @@ extension MessageReceiver {
|
|||
@discardableResult public static func insertCallInfoMessage(
|
||||
_ db: Database,
|
||||
for message: CallMessage,
|
||||
state: CallMessage.MessageInfo.State? = nil
|
||||
state: CallMessage.MessageInfo.State? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> Interaction? {
|
||||
guard
|
||||
(try? Interaction
|
||||
|
@ -300,7 +312,8 @@ extension MessageReceiver {
|
|||
threadVariant: thread.variant,
|
||||
timestampMs: (timestampMs * 1000),
|
||||
userPublicKey: currentUserPublicKey,
|
||||
openGroup: nil
|
||||
openGroup: nil,
|
||||
using: dependencies
|
||||
)
|
||||
).inserted(db)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: DataExtractionNotification
|
||||
message: DataExtractionNotification,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard
|
||||
threadVariant == .contact,
|
||||
|
@ -38,7 +39,8 @@ extension MessageReceiver {
|
|||
threadVariant: threadVariant,
|
||||
timestampMs: (timestampMs * 1000),
|
||||
userPublicKey: getUserHexEncodedPublicKey(db),
|
||||
openGroup: nil
|
||||
openGroup: nil,
|
||||
using: dependencies
|
||||
)
|
||||
).inserted(db)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: ExpirationTimerUpdate
|
||||
message: ExpirationTimerUpdate,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard !Features.useNewDisappearingMessagesConfig else { return }
|
||||
guard
|
||||
|
@ -85,7 +86,8 @@ extension MessageReceiver {
|
|||
.update(
|
||||
db,
|
||||
sessionId: threadId,
|
||||
disappearingMessagesConfig: remoteConfig
|
||||
disappearingMessagesConfig: remoteConfig,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .legacyGroup:
|
||||
|
@ -93,7 +95,8 @@ extension MessageReceiver {
|
|||
.update(
|
||||
db,
|
||||
legacyGroupPublicKey: threadId,
|
||||
disappearingConfig: remoteConfig
|
||||
disappearingConfig: remoteConfig,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
default: break
|
||||
|
@ -133,7 +136,8 @@ extension MessageReceiver {
|
|||
threadVariant: threadVariant,
|
||||
timestampMs: (timestampMs * 1000),
|
||||
userPublicKey: currentUserPublicKey,
|
||||
openGroup: nil
|
||||
openGroup: nil,
|
||||
using: dependencies
|
||||
),
|
||||
expiresInSeconds: (remoteConfig.isEnabled ? nil : localConfig.durationSeconds)
|
||||
).inserted(db)
|
||||
|
@ -144,7 +148,8 @@ extension MessageReceiver {
|
|||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: Message,
|
||||
proto: SNProtoContent
|
||||
proto: SNProtoContent,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard let sender: String = message.sender else { return }
|
||||
|
||||
|
@ -157,7 +162,8 @@ extension MessageReceiver {
|
|||
.filter(id: sender)
|
||||
.updateAllAndConfig(
|
||||
db,
|
||||
Contact.Columns.lastKnownClientVersion.set(to: lastKnownClientVersion)
|
||||
Contact.Columns.lastKnownClientVersion.set(to: lastKnownClientVersion),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
guard
|
||||
|
@ -230,7 +236,8 @@ extension MessageReceiver {
|
|||
.update(
|
||||
db,
|
||||
sessionId: threadId,
|
||||
disappearingMessagesConfig: remoteConfig
|
||||
disappearingMessagesConfig: remoteConfig,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .legacyGroup:
|
||||
|
@ -238,11 +245,18 @@ extension MessageReceiver {
|
|||
.update(
|
||||
db,
|
||||
legacyGroupPublicKey: threadId,
|
||||
disappearingConfig: remoteConfig
|
||||
disappearingConfig: remoteConfig,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .group:
|
||||
preconditionFailure()
|
||||
try SessionUtil
|
||||
.update(
|
||||
db,
|
||||
groupIdentityPublicKey: threadId,
|
||||
disappearingConfig: remoteConfig,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
default: break
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ extension MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .nameChange:
|
||||
|
@ -31,7 +32,8 @@ extension MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .membersAdded:
|
||||
|
@ -39,7 +41,8 @@ extension MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .membersRemoved:
|
||||
|
@ -47,7 +50,8 @@ extension MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .memberLeft:
|
||||
|
@ -55,7 +59,8 @@ extension MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .encryptionKeyPairRequest: break // Currently not used
|
||||
|
@ -225,7 +230,9 @@ extension MessageReceiver {
|
|||
latestKeyPairReceivedTimestamp: receivedTimestamp,
|
||||
disappearingConfig: disappearingConfig,
|
||||
members: members.asSet(),
|
||||
admins: admins.asSet()
|
||||
admins: admins.asSet(),
|
||||
formationTimestamp: TimeInterval(formationTimestampMs * 1000),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -260,7 +267,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: ClosedGroupControlMessage
|
||||
message: ClosedGroupControlMessage,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard case let .encryptionKeyPair(explicitGroupPublicKey, wrappers) = message.kind else {
|
||||
return
|
||||
|
@ -319,7 +327,8 @@ extension MessageReceiver {
|
|||
try? SessionUtil.update(
|
||||
db,
|
||||
legacyGroupPublicKey: groupPublicKey,
|
||||
latestKeyPair: keyPair
|
||||
latestKeyPair: keyPair,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
catch {
|
||||
|
@ -337,7 +346,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: ClosedGroupControlMessage
|
||||
message: ClosedGroupControlMessage,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard
|
||||
let messageKind: ClosedGroupControlMessage.Kind = message.kind,
|
||||
|
@ -356,7 +366,8 @@ extension MessageReceiver {
|
|||
try? SessionUtil.update(
|
||||
db,
|
||||
legacyGroupPublicKey: threadId,
|
||||
name: name
|
||||
name: name,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
_ = try ClosedGroup
|
||||
|
@ -373,7 +384,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: ClosedGroupControlMessage
|
||||
message: ClosedGroupControlMessage,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard
|
||||
let messageKind: ClosedGroupControlMessage.Kind = message.kind,
|
||||
|
@ -407,7 +419,8 @@ extension MessageReceiver {
|
|||
admins: allMembers
|
||||
.filter { $0.role == .admin }
|
||||
.map { $0.profileId }
|
||||
.asSet()
|
||||
.asSet(),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Create records for any new members
|
||||
|
@ -462,7 +475,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: ClosedGroupControlMessage
|
||||
message: ClosedGroupControlMessage,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard
|
||||
let messageKind: ClosedGroupControlMessage.Kind = message.kind,
|
||||
|
@ -514,7 +528,8 @@ extension MessageReceiver {
|
|||
admins: allMembers
|
||||
.filter { $0.role == .admin }
|
||||
.map { $0.profileId }
|
||||
.asSet()
|
||||
.asSet(),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Delete the removed members
|
||||
|
@ -550,7 +565,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: ClosedGroupControlMessage
|
||||
message: ClosedGroupControlMessage,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard
|
||||
let messageKind: ClosedGroupControlMessage.Kind = message.kind,
|
||||
|
@ -591,7 +607,8 @@ extension MessageReceiver {
|
|||
admins: allMembers
|
||||
.filter { $0.role == .admin }
|
||||
.map { $0.profileId }
|
||||
.asSet()
|
||||
.asSet(),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Delete the members to remove
|
||||
|
|
|
@ -106,7 +106,8 @@ extension MessageReceiver {
|
|||
threadId: blindedIdLookup.blindedId,
|
||||
threadVariant: .contact,
|
||||
groupLeaveType: .forced,
|
||||
calledFromConfigHandling: false
|
||||
calledFromConfigHandling: false,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ extension MessageReceiver {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
message: UnsendRequest
|
||||
message: UnsendRequest,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws {
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
|
||||
|
@ -35,7 +36,8 @@ extension MessageReceiver {
|
|||
threadId: interaction.threadId,
|
||||
threadVariant: threadVariant,
|
||||
includingOlder: false,
|
||||
trySendReadReceipt: false
|
||||
trySendReadReceipt: false,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: interaction.notificationIdentifiers)
|
||||
|
@ -46,9 +48,10 @@ extension MessageReceiver {
|
|||
SnodeAPI
|
||||
.deleteMessages(
|
||||
publicKey: author,
|
||||
serverHashes: [serverHash]
|
||||
serverHashes: [serverHash],
|
||||
using: dependencies
|
||||
)
|
||||
.subscribe(on: DispatchQueue.global(qos: .background))
|
||||
.subscribe(on: DispatchQueue.global(qos: .background), using: dependencies)
|
||||
.sinkUntilComplete()
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ extension MessageReceiver {
|
|||
|
||||
guard
|
||||
let userEdKeyPair: KeyPair = Identity.fetchUserEd25519KeyPair(db),
|
||||
let blindedKeyPair: KeyPair = try? dependencies.crypto.generate(
|
||||
let blindedKeyPair: KeyPair = dependencies.crypto.generate(
|
||||
.blindedKeyPair(
|
||||
serverPublicKey: openGroup.publicKey,
|
||||
edKeyPair: userEdKeyPair,
|
||||
|
@ -132,7 +132,8 @@ extension MessageReceiver {
|
|||
associatedWithProto: proto,
|
||||
sender: sender,
|
||||
messageSentTimestamp: messageSentTimestamp,
|
||||
openGroup: maybeOpenGroup
|
||||
openGroup: maybeOpenGroup,
|
||||
using: dependencies
|
||||
) {
|
||||
return interactionId
|
||||
}
|
||||
|
@ -159,7 +160,8 @@ extension MessageReceiver {
|
|||
threadVariant: thread.variant,
|
||||
timestampMs: Int64(messageSentTimestamp * 1000),
|
||||
userPublicKey: currentUserPublicKey,
|
||||
openGroup: maybeOpenGroup
|
||||
openGroup: maybeOpenGroup,
|
||||
using: dependencies
|
||||
)
|
||||
),
|
||||
hasMention: Interaction.isUserMentioned(
|
||||
|
@ -208,7 +210,8 @@ extension MessageReceiver {
|
|||
interactionId: existingInteractionId,
|
||||
messageSentTimestamp: messageSentTimestamp,
|
||||
variant: variant,
|
||||
syncTarget: message.syncTarget
|
||||
syncTarget: message.syncTarget,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
getExpirationForOutgoingDisappearingMessages(
|
||||
|
@ -234,7 +237,8 @@ extension MessageReceiver {
|
|||
interactionId: interactionId,
|
||||
messageSentTimestamp: messageSentTimestamp,
|
||||
variant: variant,
|
||||
syncTarget: message.syncTarget
|
||||
syncTarget: message.syncTarget,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
getExpirationForOutgoingDisappearingMessages(
|
||||
|
@ -366,7 +370,8 @@ extension MessageReceiver {
|
|||
associatedWithProto proto: SNProtoContent,
|
||||
sender: String,
|
||||
messageSentTimestamp: TimeInterval,
|
||||
openGroup: OpenGroup?
|
||||
openGroup: OpenGroup?,
|
||||
using dependencies: Dependencies
|
||||
) throws -> Int64? {
|
||||
guard
|
||||
let reaction: VisibleMessage.VMReaction = message.reaction,
|
||||
|
@ -413,7 +418,8 @@ extension MessageReceiver {
|
|||
threadVariant: thread.variant,
|
||||
timestampMs: timestampMs,
|
||||
userPublicKey: currentUserPublicKey,
|
||||
openGroup: openGroup
|
||||
openGroup: openGroup,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Don't notify if the reaction was added before the lastest read timestamp for
|
||||
|
@ -444,7 +450,8 @@ extension MessageReceiver {
|
|||
interactionId: Int64,
|
||||
messageSentTimestamp: TimeInterval,
|
||||
variant: Interaction.Variant,
|
||||
syncTarget: String?
|
||||
syncTarget: String?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard variant == .standardOutgoing else { return }
|
||||
|
||||
|
@ -494,7 +501,8 @@ extension MessageReceiver {
|
|||
threadId: thread.id,
|
||||
threadVariant: thread.variant,
|
||||
includingOlder: true,
|
||||
trySendReadReceipt: false
|
||||
trySendReadReceipt: false,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Process any PendingReadReceipt values
|
||||
|
|
|
@ -12,13 +12,13 @@ extension MessageSender {
|
|||
thread: SessionThread,
|
||||
group: ClosedGroup,
|
||||
members: [GroupMember],
|
||||
preparedNotificationsSubscription: HTTP.PreparedRequest<PushNotificationAPI.SubscribeResponse>,
|
||||
preparedNotificationsSubscription: HTTP.PreparedRequest<PushNotificationAPI.SubscribeResponse>?,
|
||||
currentUserPublicKey: String
|
||||
)
|
||||
public static func createGroup(
|
||||
name: String,
|
||||
displayPicture: SignalAttachment?,
|
||||
members: Set<String>,
|
||||
members: [(String, Profile?)],
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<SessionThread, Error> {
|
||||
Just(())
|
||||
|
@ -38,6 +38,7 @@ extension MessageSender {
|
|||
dependencies.storage.write { db -> PreparedGroupData in
|
||||
// Create and cache the libSession entries
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let currentUserProfile: Profile = Profile.fetchOrCreateCurrentUser(db, using: dependencies)
|
||||
let groupData: (identityKeyPair: KeyPair, group: ClosedGroup, members: [GroupMember]) = try SessionUtil.createGroup(
|
||||
db,
|
||||
name: name,
|
||||
|
@ -45,10 +46,10 @@ extension MessageSender {
|
|||
displayPictureFilename: displayPictureInfo?.filename,
|
||||
displayPictureEncryptionKey: displayPictureInfo?.encryptionKey,
|
||||
members: members,
|
||||
admins: [currentUserPublicKey],
|
||||
admins: [(currentUserPublicKey, currentUserProfile)],
|
||||
using: dependencies
|
||||
)
|
||||
let preparedNotificationSubscription = try PushNotificationAPI
|
||||
let preparedNotificationSubscription = try? PushNotificationAPI
|
||||
.preparedSubscribe(
|
||||
publicKey: groupData.group.id,
|
||||
subkey: nil,
|
||||
|
@ -58,7 +59,13 @@ extension MessageSender {
|
|||
|
||||
// Save the relevant objects to the database
|
||||
let thread: SessionThread = try SessionThread
|
||||
.fetchOrCreate(db, id: groupData.group.id, variant: .group, shouldBeVisible: true)
|
||||
.fetchOrCreate(
|
||||
db,
|
||||
id: groupData.group.id,
|
||||
variant: .group,
|
||||
shouldBeVisible: true,
|
||||
using: dependencies
|
||||
)
|
||||
try groupData.group.insert(db)
|
||||
try groupData.members.forEach { try $0.insert(db) }
|
||||
|
||||
|
@ -86,8 +93,8 @@ extension MessageSender {
|
|||
// Start polling
|
||||
ClosedGroupPoller.shared.startIfNeeded(for: group.id, using: dependencies)
|
||||
|
||||
// Subscribe for push notifications
|
||||
preparedNotificationSubscription
|
||||
// Subscribe for push notifications (if PNs are enabled)
|
||||
preparedNotificationSubscription?
|
||||
.send(using: dependencies)
|
||||
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
|
||||
.sinkUntilComplete()
|
||||
|
|
|
@ -83,7 +83,9 @@ extension MessageSender {
|
|||
latestKeyPairReceivedTimestamp: latestKeyPairReceivedTimestamp,
|
||||
disappearingConfig: DisappearingMessagesConfiguration.defaultWith(legacyGroupPublicKey),
|
||||
members: members,
|
||||
admins: admins
|
||||
admins: admins,
|
||||
formationTimestamp: formationTimestamp,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
let memberSendData: [MessageSender.PreparedSendData] = try members
|
||||
|
@ -249,7 +251,8 @@ extension MessageSender {
|
|||
admins: allGroupMembers
|
||||
.filter { $0.role == .admin }
|
||||
.map { $0.profileId }
|
||||
.asSet()
|
||||
.asSet(),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -271,7 +274,7 @@ extension MessageSender {
|
|||
name: String,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Void, Error> {
|
||||
return Storage.shared
|
||||
return dependencies.storage
|
||||
.writePublisher { db -> (String, ClosedGroup, [GroupMember], Set<String>) in
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
|
||||
|
@ -318,7 +321,8 @@ extension MessageSender {
|
|||
try? SessionUtil.update(
|
||||
db,
|
||||
legacyGroupPublicKey: closedGroup.threadId,
|
||||
name: name
|
||||
name: name,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -430,7 +434,8 @@ extension MessageSender {
|
|||
admins: allGroupMembers
|
||||
.filter { $0.role == .admin }
|
||||
.map { $0.profileId }
|
||||
.asSet()
|
||||
.asSet(),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
// Send the update to the group
|
||||
|
|
|
@ -200,7 +200,7 @@ public enum MessageReceiver {
|
|||
// the config then the message will be dropped)
|
||||
guard
|
||||
!Message.requiresExistingConversation(message: message, threadVariant: threadVariant) ||
|
||||
SessionUtil.conversationInConfig(db, threadId: threadId, threadVariant: threadVariant, visibleOnly: false)
|
||||
SessionUtil.conversationInConfig(db, threadId: threadId, threadVariant: threadVariant, visibleOnly: false, using: dependencies)
|
||||
else { throw MessageReceiverError.requiredThreadNotInConfig }
|
||||
|
||||
// Throw if the message is outdated and shouldn't be processed
|
||||
|
@ -221,7 +221,8 @@ public enum MessageReceiver {
|
|||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message,
|
||||
proto: proto
|
||||
proto: proto,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
switch message {
|
||||
|
@ -254,7 +255,8 @@ public enum MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case let message as ExpirationTimerUpdate:
|
||||
|
@ -262,7 +264,8 @@ public enum MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case let message as UnsendRequest:
|
||||
|
@ -270,7 +273,8 @@ public enum MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case let message as CallMessage:
|
||||
|
@ -278,7 +282,8 @@ public enum MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
message: message
|
||||
message: message,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case let message as MessageRequestResponse:
|
||||
|
@ -403,7 +408,8 @@ public enum MessageReceiver {
|
|||
db,
|
||||
threadId: threadId,
|
||||
threadVariant: threadVariant,
|
||||
visibleOnly: true
|
||||
visibleOnly: true,
|
||||
using: dependencies
|
||||
)
|
||||
let canPerformChange: Bool = SessionUtil.canPerformChange(
|
||||
db,
|
||||
|
|
|
@ -183,7 +183,7 @@ public enum PushNotificationAPI {
|
|||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> HTTP.PreparedRequest<SubscribeResponse> {
|
||||
guard
|
||||
UserDefaults.standard[.isUsingFullAPNs],
|
||||
dependencies.standardUserDefaults[.isUsingFullAPNs],
|
||||
let token: String = dependencies.standardUserDefaults[.deviceToken]
|
||||
else { throw HTTPError.invalidRequest }
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ public final class ClosedGroupPoller: Poller {
|
|||
override func nextPollDelay(for publicKey: String, using dependencies: Dependencies) -> TimeInterval {
|
||||
// Get the received date of the last message in the thread. If we don't have
|
||||
// any messages yet, pick some reasonable fake time interval to use instead
|
||||
let lastMessageDate: Date = Storage.shared
|
||||
let lastMessageDate: Date = dependencies.storage
|
||||
.read { db in
|
||||
try Interaction
|
||||
.filter(Interaction.Columns.threadId == publicKey)
|
||||
|
@ -74,7 +74,7 @@ public final class ClosedGroupPoller: Poller {
|
|||
|
||||
return Date(timeIntervalSince1970: (TimeInterval(receivedAtTimestampMs) / 1000))
|
||||
}
|
||||
.defaulting(to: Date().addingTimeInterval(-5 * 60))
|
||||
.defaulting(to: dependencies.dateNow.addingTimeInterval(-5 * 60))
|
||||
|
||||
let timeSinceLastMessage: TimeInterval = dependencies.dateNow.timeIntervalSince(lastMessageDate)
|
||||
let minPollInterval: Double = ClosedGroupPoller.minPollInterval
|
||||
|
|
|
@ -67,7 +67,7 @@ public final class CurrentUserPoller: Poller {
|
|||
else if let targetSnode: Snode = targetSnode.wrappedValue {
|
||||
SNLog("Main Poller polling \(targetSnode) failed; dropping it and switching to next snode.")
|
||||
self.targetSnode.mutate { $0 = nil }
|
||||
SnodeAPI.dropSnodeFromSwarmIfNeeded(targetSnode, publicKey: publicKey)
|
||||
SnodeAPI.dropSnodeFromSwarmIfNeeded(targetSnode, publicKey: publicKey, using: dependencies)
|
||||
}
|
||||
else {
|
||||
SNLog("Polling failed due to having no target service node.")
|
||||
|
|
|
@ -67,7 +67,7 @@ public class Poller {
|
|||
internal func startIfNeeded(for publicKey: String, using dependencies: Dependencies) {
|
||||
// Run on the 'pollerQueue' to ensure any 'Atomic' access doesn't block the main thread
|
||||
// on startup
|
||||
Threading.pollerQueue.async { [weak self] in
|
||||
Threading.pollerQueue.async(using: dependencies) { [weak self] in
|
||||
guard self?.isPolling.wrappedValue[publicKey] != true else { return }
|
||||
|
||||
// Might be a race condition that the setUpPolling finishes too soon,
|
||||
|
@ -225,7 +225,7 @@ public class Poller {
|
|||
poller?.pollerName(for: publicKey) ??
|
||||
"poller with public key \(publicKey)"
|
||||
)
|
||||
let configHashes: [String] = SessionUtil.configHashes(for: publicKey)
|
||||
let configHashes: [String] = SessionUtil.configHashes(for: publicKey, using: dependencies)
|
||||
|
||||
// Fetch the messages
|
||||
return SnodeAPI
|
||||
|
|
|
@ -33,16 +33,16 @@ internal extension SessionUtil {
|
|||
|
||||
static func handleContactsUpdate(
|
||||
_ db: Database,
|
||||
in conf: UnsafeMutablePointer<config_object>?,
|
||||
mergeNeedsDump: Bool,
|
||||
latestConfigSentTimestampMs: Int64
|
||||
in config: Config?,
|
||||
latestConfigSentTimestampMs: Int64,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard mergeNeedsDump else { return }
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard config.needsDump else { return }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
// The current users contact data is handled separately so exclude it if it's present (as that's
|
||||
// actually a bug)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let targetContactData: [String: ContactData] = try extractContacts(
|
||||
from: conf,
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs
|
||||
|
@ -151,7 +151,8 @@ internal extension SessionUtil {
|
|||
threadId: sessionId,
|
||||
threadVariant: .contact,
|
||||
groupLeaveType: .forced,
|
||||
calledFromConfigHandling: true
|
||||
calledFromConfigHandling: true,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case (true, false):
|
||||
|
@ -270,10 +271,11 @@ internal extension SessionUtil {
|
|||
threadIds: combinedIds,
|
||||
threadVariant: .contact,
|
||||
groupLeaveType: .forced,
|
||||
calledFromConfigHandling: true
|
||||
calledFromConfigHandling: true,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
try SessionUtil.remove(db, volatileContactIds: combinedIds)
|
||||
try SessionUtil.remove(db, volatileContactIds: combinedIds, using: dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,9 +283,9 @@ internal extension SessionUtil {
|
|||
|
||||
static func upsert(
|
||||
contactData: [SyncedContactInfo],
|
||||
in conf: UnsafeMutablePointer<config_object>?
|
||||
in config: Config?
|
||||
) throws {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
// The current users contact data doesn't need to sync so exclude it, we also don't want to sync
|
||||
// blinded message requests so exclude those as well
|
||||
|
@ -305,7 +307,7 @@ internal extension SessionUtil {
|
|||
guard contacts_get_or_construct(conf, &contact, &sessionId) else {
|
||||
/// It looks like there are some situations where this object might not get created correctly (and
|
||||
/// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead
|
||||
SNLog("Unable to upsert contact to SessionUtil: \(SessionUtil.lastError(conf))")
|
||||
SNLog("Unable to upsert contact to SessionUtil: \(config.lastError)")
|
||||
throw SessionUtilError.getOrConstructFailedUnexpectedly
|
||||
}
|
||||
|
||||
|
@ -374,12 +376,16 @@ internal extension SessionUtil {
|
|||
// MARK: - Outgoing Changes
|
||||
|
||||
internal extension SessionUtil {
|
||||
static func updatingContacts<T>(_ db: Database, _ updated: [T]) throws -> [T] {
|
||||
static func updatingContacts<T>(
|
||||
_ db: Database,
|
||||
_ updated: [T],
|
||||
using dependencies: Dependencies
|
||||
) throws -> [T] {
|
||||
guard let updatedContacts: [Contact] = updated as? [Contact] else { throw StorageError.generic }
|
||||
|
||||
// The current users contact data doesn't need to sync so exclude it, we also don't want to sync
|
||||
// blinded message requests so exclude those as well
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let targetContacts: [Contact] = updatedContacts
|
||||
.filter {
|
||||
$0.id != userPublicKey &&
|
||||
|
@ -392,8 +398,11 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .contacts,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
// When inserting new contacts (or contacts with invalid profile data) we want
|
||||
// to add any valid profile information we have so identify if any of the updated
|
||||
// contacts are new/invalid, and if so, fetch any profile data we have for them
|
||||
|
@ -424,14 +433,18 @@ internal extension SessionUtil {
|
|||
profile: newProfiles[contact.id]
|
||||
)
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
static func updatingProfiles<T>(_ db: Database, _ updated: [T]) throws -> [T] {
|
||||
static func updatingProfiles<T>(
|
||||
_ db: Database,
|
||||
_ updated: [T],
|
||||
using dependencies: Dependencies
|
||||
) throws -> [T] {
|
||||
guard let updatedProfiles: [Profile] = updated as? [Profile] else { throw StorageError.generic }
|
||||
|
||||
// We should only sync profiles which are associated to contact data to avoid including profiles
|
||||
|
@ -449,7 +462,7 @@ internal extension SessionUtil {
|
|||
guard !existingContactIds.isEmpty else { return updated }
|
||||
|
||||
// Get the user public key (updating their profile is handled separately)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let targetProfiles: [Profile] = updatedProfiles
|
||||
.filter {
|
||||
$0.id != userPublicKey &&
|
||||
|
@ -462,11 +475,12 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userProfile,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.update(
|
||||
profile: updatedUserProfile,
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -474,20 +488,25 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .contacts,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil
|
||||
.upsert(
|
||||
contactData: targetProfiles
|
||||
.map { SyncedContactInfo(id: $0.id, profile: $0) },
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
return updated
|
||||
}
|
||||
|
||||
static func updatingDisappearingConfigsOneToOne<T>(_ db: Database, _ updated: [T]) throws -> [T] {
|
||||
static func updatingDisappearingConfigsOneToOne<T>(
|
||||
_ db: Database,
|
||||
_ updated: [T],
|
||||
using dependencies: Dependencies
|
||||
) throws -> [T] {
|
||||
guard let updatedDisappearingConfigs: [DisappearingMessagesConfiguration] = updated as? [DisappearingMessagesConfiguration] else { throw StorageError.generic }
|
||||
|
||||
// Filter out any disappearing config changes related to groups
|
||||
|
@ -509,7 +528,7 @@ internal extension SessionUtil {
|
|||
guard !existingContactIds.isEmpty else { return updated }
|
||||
|
||||
// Get the user public key (updating note to self is handled separately)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let targetDisappearingConfigs: [DisappearingMessagesConfiguration] = targetUpdatedConfigs
|
||||
.filter {
|
||||
$0.id != userPublicKey &&
|
||||
|
@ -522,11 +541,12 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userProfile,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.updateNoteToSelf(
|
||||
disappearingMessagesConfig: updatedUserDisappearingConfig,
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -534,13 +554,14 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .contacts,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil
|
||||
.upsert(
|
||||
contactData: targetDisappearingConfigs
|
||||
.map { SyncedContactInfo(id: $0.id, disappearingMessagesConfig: $0) },
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -551,12 +572,17 @@ internal extension SessionUtil {
|
|||
// MARK: - External Outgoing Changes
|
||||
|
||||
public extension SessionUtil {
|
||||
static func hide(_ db: Database, contactIds: [String]) throws {
|
||||
static func hide(
|
||||
_ db: Database,
|
||||
contactIds: [String],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .contacts,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
// Mark the contacts as hidden
|
||||
try SessionUtil.upsert(
|
||||
contactData: contactIds
|
||||
|
@ -566,19 +592,26 @@ public extension SessionUtil {
|
|||
priority: SessionUtil.hiddenPriority
|
||||
)
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static func remove(_ db: Database, contactIds: [String]) throws {
|
||||
static func remove(
|
||||
_ db: Database,
|
||||
contactIds: [String],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard !contactIds.isEmpty else { return }
|
||||
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .contacts,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
contactIds.forEach { sessionId in
|
||||
var cSessionId: [CChar] = sessionId.cArray.nullTerminated()
|
||||
|
||||
|
@ -591,20 +624,22 @@ public extension SessionUtil {
|
|||
static func update(
|
||||
_ db: Database,
|
||||
sessionId: String,
|
||||
disappearingMessagesConfig: DisappearingMessagesConfiguration
|
||||
disappearingMessagesConfig: DisappearingMessagesConfiguration,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
|
||||
switch sessionId {
|
||||
case userPublicKey:
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userProfile,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.updateNoteToSelf(
|
||||
disappearingMessagesConfig: disappearingMessagesConfig,
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -612,8 +647,9 @@ public extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .contacts,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil
|
||||
.upsert(
|
||||
contactData: [
|
||||
|
@ -622,7 +658,7 @@ public extension SessionUtil {
|
|||
disappearingMessagesConfig: disappearingMessagesConfig
|
||||
)
|
||||
],
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@ internal extension SessionUtil {
|
|||
|
||||
static func handleConvoInfoVolatileUpdate(
|
||||
_ db: Database,
|
||||
in conf: UnsafeMutablePointer<config_object>?,
|
||||
mergeNeedsDump: Bool
|
||||
in config: Config?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard mergeNeedsDump else { return }
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard config.needsDump else { return }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
// Get the volatile thread info from the conf and local conversations
|
||||
let volatileThreadInfo: [VolatileThreadInfo] = try extractConvoVolatileInfo(from: conf)
|
||||
|
@ -101,15 +101,15 @@ internal extension SessionUtil {
|
|||
|
||||
try upsert(
|
||||
convoInfoVolatileChanges: newerLocalChanges,
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
static func upsert(
|
||||
convoInfoVolatileChanges: [VolatileThreadInfo],
|
||||
in conf: UnsafeMutablePointer<config_object>?
|
||||
in config: Config?
|
||||
) throws {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
// Exclude any invalid thread info
|
||||
let validChanges: [VolatileThreadInfo] = convoInfoVolatileChanges
|
||||
|
@ -135,7 +135,7 @@ internal extension SessionUtil {
|
|||
guard convo_info_volatile_get_or_construct_1to1(conf, &oneToOne, &cThreadId) else {
|
||||
/// It looks like there are some situations where this object might not get created correctly (and
|
||||
/// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead
|
||||
SNLog("Unable to upsert contact volatile info to SessionUtil: \(SessionUtil.lastError(conf))")
|
||||
SNLog("Unable to upsert contact volatile info to SessionUtil: \(config.lastError)")
|
||||
throw SessionUtilError.getOrConstructFailedUnexpectedly
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ internal extension SessionUtil {
|
|||
guard convo_info_volatile_get_or_construct_legacy_group(conf, &legacyGroup, &cThreadId) else {
|
||||
/// It looks like there are some situations where this object might not get created correctly (and
|
||||
/// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead
|
||||
SNLog("Unable to upsert legacy group volatile info to SessionUtil: \(SessionUtil.lastError(conf))")
|
||||
SNLog("Unable to upsert legacy group volatile info to SessionUtil: \(config.lastError)")
|
||||
throw SessionUtilError.getOrConstructFailedUnexpectedly
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,7 @@ internal extension SessionUtil {
|
|||
guard convo_info_volatile_get_or_construct_community(conf, &community, &cBaseUrl, &cRoomToken, &cPubkey) else {
|
||||
/// It looks like there are some situations where this object might not get created correctly (and
|
||||
/// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead
|
||||
SNLog("Unable to upsert community volatile info to SessionUtil: \(SessionUtil.lastError(conf))")
|
||||
SNLog("Unable to upsert community volatile info to SessionUtil: \(config.lastError)")
|
||||
throw SessionUtilError.getOrConstructFailedUnexpectedly
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,8 @@ internal extension SessionUtil {
|
|||
|
||||
static func updateMarkedAsUnreadState(
|
||||
_ db: Database,
|
||||
threads: [SessionThread]
|
||||
threads: [SessionThread],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
// If we have no updated threads then no need to continue
|
||||
guard !threads.isEmpty else { return }
|
||||
|
@ -227,21 +228,29 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .convoInfoVolatile,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
try upsert(
|
||||
convoInfoVolatileChanges: changes,
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static func remove(_ db: Database, volatileContactIds: [String]) throws {
|
||||
static func remove(
|
||||
_ db: Database,
|
||||
volatileContactIds: [String],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .convoInfoVolatile,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
volatileContactIds.forEach { contactId in
|
||||
var cSessionId: [CChar] = contactId.cArray.nullTerminated()
|
||||
|
||||
|
@ -251,12 +260,19 @@ internal extension SessionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static func remove(_ db: Database, volatileLegacyGroupIds: [String]) throws {
|
||||
static func remove(
|
||||
_ db: Database,
|
||||
volatileLegacyGroupIds: [String],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .convoInfoVolatile,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
volatileLegacyGroupIds.forEach { legacyGroupId in
|
||||
var cLegacyGroupId: [CChar] = legacyGroupId.cArray.nullTerminated()
|
||||
|
||||
|
@ -266,12 +282,35 @@ internal extension SessionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
static func remove(_ db: Database, volatileCommunityInfo: [OpenGroupUrlInfo]) throws {
|
||||
static func remove(
|
||||
_ db: Database,
|
||||
volatileGroupIds: [String],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .convoInfoVolatile,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static func remove(
|
||||
_ db: Database,
|
||||
volatileCommunityInfo: [OpenGroupUrlInfo],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .convoInfoVolatile,
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
volatileCommunityInfo.forEach { urlInfo in
|
||||
var cBaseUrl: [CChar] = urlInfo.server.cArray.nullTerminated()
|
||||
var cRoom: [CChar] = urlInfo.roomToken.cArray.nullTerminated()
|
||||
|
@ -290,13 +329,15 @@ public extension SessionUtil {
|
|||
_ db: Database,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
lastReadTimestampMs: Int64
|
||||
lastReadTimestampMs: Int64,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .convoInfoVolatile,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
try upsert(
|
||||
convoInfoVolatileChanges: [
|
||||
VolatileThreadInfo(
|
||||
|
@ -308,7 +349,7 @@ public extension SessionUtil {
|
|||
changes: [.lastReadTimestampMs(lastReadTimestampMs)]
|
||||
)
|
||||
],
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -318,12 +359,15 @@ public extension SessionUtil {
|
|||
threadVariant: SessionThread.Variant,
|
||||
timestampMs: Int64,
|
||||
userPublicKey: String,
|
||||
openGroup: OpenGroup?
|
||||
openGroup: OpenGroup?,
|
||||
using dependencies: Dependencies
|
||||
) -> Bool {
|
||||
return SessionUtil
|
||||
return dependencies.caches[.sessionUtil]
|
||||
.config(for: .convoInfoVolatile, publicKey: userPublicKey)
|
||||
.wrappedValue
|
||||
.map { conf in
|
||||
.map { config in
|
||||
guard case .object(let conf) = config else { return false }
|
||||
|
||||
switch threadVariant {
|
||||
case .contact:
|
||||
var cThreadId: [CChar] = threadId.cArray.nullTerminated()
|
||||
|
|
|
@ -21,21 +21,25 @@ internal extension SessionUtil {
|
|||
|
||||
static func handleGroupInfoUpdate(
|
||||
_ db: Database,
|
||||
in conf: UnsafeMutablePointer<config_object>?,
|
||||
mergeNeedsDump: Bool,
|
||||
latestConfigSentTimestampMs: Int64
|
||||
in config: Config?,
|
||||
latestConfigSentTimestampMs: Int64,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
typealias GroupData = (profileName: String, profilePictureUrl: String?, profilePictureKey: Data?)
|
||||
|
||||
guard mergeNeedsDump else { return }
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard config.needsDump else { return }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Outgoing Changes
|
||||
|
||||
internal extension SessionUtil {
|
||||
static func updatingGroupInfo<T>(_ db: Database, _ updated: [T]) throws -> [T] {
|
||||
static func updatingGroupInfo<T>(
|
||||
_ db: Database,
|
||||
_ updated: [T],
|
||||
using dependencies: Dependencies
|
||||
) throws -> [T] {
|
||||
guard let updatedGroups: [ClosedGroup] = updated as? [ClosedGroup] else { throw StorageError.generic }
|
||||
|
||||
// Exclude legacy groups as they aren't managed via SessionUtil
|
||||
|
@ -50,8 +54,11 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .groupInfo,
|
||||
publicKey: group.threadId
|
||||
) { conf in
|
||||
publicKey: group.threadId,
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
// Update the name
|
||||
var updatedName: [CChar] = group.name.cArray.nullTerminated()
|
||||
groups_info_set_name(conf, &updatedName)
|
||||
|
@ -67,7 +74,11 @@ internal extension SessionUtil {
|
|||
return updated
|
||||
}
|
||||
|
||||
static func updatingDisappearingConfigsGroups<T>(_ db: Database, _ updated: [T]) throws -> [T] {
|
||||
static func updatingDisappearingConfigsGroups<T>(
|
||||
_ db: Database,
|
||||
_ updated: [T],
|
||||
using dependencies: Dependencies
|
||||
) throws -> [T] {
|
||||
guard let updatedDisappearingConfigs: [DisappearingMessagesConfiguration] = updated as? [DisappearingMessagesConfiguration] else { throw StorageError.generic }
|
||||
|
||||
// Filter out any disappearing config changes not related to updated groups
|
||||
|
@ -95,8 +106,11 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .groupInfo,
|
||||
publicKey: groupIdentityPublicKey
|
||||
) { conf in
|
||||
publicKey: groupIdentityPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
groups_info_set_expiry_timer(conf, Int32(updatedConfig.durationSeconds))
|
||||
}
|
||||
}
|
||||
|
@ -112,13 +126,17 @@ public extension SessionUtil {
|
|||
_ db: Database,
|
||||
groupIdentityPublicKey: String,
|
||||
name: String? = nil,
|
||||
disappearingConfig: DisappearingMessagesConfiguration? = nil
|
||||
disappearingConfig: DisappearingMessagesConfiguration? = nil,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .groupInfo,
|
||||
publicKey: groupIdentityPublicKey
|
||||
) { conf in
|
||||
publicKey: groupIdentityPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
if let name: String = name {
|
||||
groups_info_set_name(conf, name.toLibSession())
|
||||
}
|
||||
|
|
|
@ -14,12 +14,12 @@ internal extension SessionUtil {
|
|||
|
||||
static func handleGroupKeysUpdate(
|
||||
_ db: Database,
|
||||
in conf: UnsafeMutablePointer<config_object>?,
|
||||
mergeNeedsDump: Bool,
|
||||
latestConfigSentTimestampMs: Int64
|
||||
in config: Config?,
|
||||
latestConfigSentTimestampMs: Int64,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard mergeNeedsDump else { return }
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard config.needsDump else { return }
|
||||
guard case .groupKeys(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,12 +14,12 @@ internal extension SessionUtil {
|
|||
|
||||
static func handleGroupMembersUpdate(
|
||||
_ db: Database,
|
||||
in conf: UnsafeMutablePointer<config_object>?,
|
||||
mergeNeedsDump: Bool,
|
||||
latestConfigSentTimestampMs: Int64
|
||||
in config: Config?,
|
||||
latestConfigSentTimestampMs: Int64,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard mergeNeedsDump else { return }
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard config.needsDump else { return }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,36 +47,44 @@ internal extension SessionUtil {
|
|||
return (priority >= SessionUtil.visiblePriority)
|
||||
}
|
||||
|
||||
static func pushChangesIfNeeded(
|
||||
_ db: Database,
|
||||
for variant: ConfigDump.Variant,
|
||||
publicKey: String,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try performAndPushChange(db, for: variant, publicKey: publicKey, using: dependencies) { _ in }
|
||||
}
|
||||
|
||||
static func performAndPushChange(
|
||||
_ db: Database,
|
||||
for variant: ConfigDump.Variant,
|
||||
publicKey: String,
|
||||
change: (UnsafeMutablePointer<config_object>?) throws -> ()
|
||||
using dependencies: Dependencies,
|
||||
change: (Config?) throws -> ()
|
||||
) throws {
|
||||
// Since we are doing direct memory manipulation we are using an `Atomic`
|
||||
// type which has blocking access in it's `mutate` closure
|
||||
let needsPush: Bool
|
||||
|
||||
do {
|
||||
needsPush = try SessionUtil
|
||||
needsPush = try dependencies.caches[.sessionUtil]
|
||||
.config(for: variant, publicKey: publicKey)
|
||||
.mutate { conf in
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
|
||||
.mutate { config in
|
||||
// Peform the change
|
||||
try change(conf)
|
||||
|
||||
try change(config)
|
||||
|
||||
// If we don't need to dump the data the we can finish early
|
||||
guard config_needs_dump(conf) else { return config_needs_push(conf) }
|
||||
|
||||
guard config.needsDump else { return config.needsPush }
|
||||
|
||||
try SessionUtil.createDump(
|
||||
conf: conf,
|
||||
config: config,
|
||||
for: variant,
|
||||
publicKey: publicKey,
|
||||
timestampMs: SnodeAPI.currentOffsetTimestampMs()
|
||||
)?.save(db)
|
||||
|
||||
return config_needs_push(conf)
|
||||
|
||||
return config.needsPush
|
||||
}
|
||||
}
|
||||
catch {
|
||||
|
@ -92,7 +100,11 @@ internal extension SessionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
@discardableResult static func updatingThreads<T>(_ db: Database, _ updated: [T]) throws -> [T] {
|
||||
@discardableResult static func updatingThreads<T>(
|
||||
_ db: Database,
|
||||
_ updated: [T],
|
||||
using dependencies: Dependencies
|
||||
) throws -> [T] {
|
||||
guard let updatedThreads: [SessionThread] = updated as? [SessionThread] else {
|
||||
throw StorageError.generic
|
||||
}
|
||||
|
@ -108,7 +120,7 @@ internal extension SessionUtil {
|
|||
.reduce(into: [:]) { result, next in result[next.threadId] = next }
|
||||
|
||||
// Update the unread state for the threads first (just in case that's what changed)
|
||||
try SessionUtil.updateMarkedAsUnreadState(db, threads: updatedThreads)
|
||||
try SessionUtil.updateMarkedAsUnreadState(db, threads: updatedThreads, using: dependencies)
|
||||
|
||||
// Then update the `hidden` and `priority` values
|
||||
try groupedThreads.forEach { variant, threads in
|
||||
|
@ -120,8 +132,9 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userProfile,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.updateNoteToSelf(
|
||||
priority: {
|
||||
guard noteToSelf.shouldBeVisible else { return SessionUtil.hiddenPriority }
|
||||
|
@ -130,7 +143,7 @@ internal extension SessionUtil {
|
|||
.map { Int32($0 == 0 ? SessionUtil.visiblePriority : max($0, 1)) }
|
||||
.defaulting(to: SessionUtil.visiblePriority)
|
||||
}(),
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -143,8 +156,9 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .contacts,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.upsert(
|
||||
contactData: remainingThreads
|
||||
.map { thread in
|
||||
|
@ -159,7 +173,7 @@ internal extension SessionUtil {
|
|||
}()
|
||||
)
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -167,8 +181,9 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.upsert(
|
||||
communities: threads
|
||||
.compactMap { thread -> CommunityInfo? in
|
||||
|
@ -181,7 +196,7 @@ internal extension SessionUtil {
|
|||
)
|
||||
}
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -189,8 +204,9 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.upsert(
|
||||
legacyGroups: threads
|
||||
.map { thread in
|
||||
|
@ -201,7 +217,7 @@ internal extension SessionUtil {
|
|||
.defaulting(to: SessionUtil.visiblePriority)
|
||||
)
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -209,8 +225,9 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.upsert(
|
||||
groups: threads
|
||||
.map { thread in
|
||||
|
@ -221,7 +238,7 @@ internal extension SessionUtil {
|
|||
.defaulting(to: SessionUtil.visiblePriority)
|
||||
)
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -230,23 +247,31 @@ internal extension SessionUtil {
|
|||
return updated
|
||||
}
|
||||
|
||||
static func hasSetting(_ db: Database, forKey key: String) throws -> Bool {
|
||||
static func hasSetting(
|
||||
_ db: Database,
|
||||
forKey key: String,
|
||||
using dependencies: Dependencies
|
||||
) throws -> Bool {
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
|
||||
// Currently the only synced setting is 'checkForCommunityMessageRequests'
|
||||
switch key {
|
||||
case Setting.BoolKey.checkForCommunityMessageRequests.rawValue:
|
||||
return try SessionUtil
|
||||
return try dependencies.caches[.sessionUtil]
|
||||
.config(for: .userProfile, publicKey: userPublicKey)
|
||||
.wrappedValue
|
||||
.map { conf -> Bool in (try SessionUtil.rawBlindedMessageRequestValue(in: conf) >= 0) }
|
||||
.map { config -> Bool in (try SessionUtil.rawBlindedMessageRequestValue(in: config) >= 0) }
|
||||
.defaulting(to: false)
|
||||
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
static func updatingSetting(_ db: Database, _ updated: Setting?) throws {
|
||||
static func updatingSetting(
|
||||
_ db: Database,
|
||||
_ updated: Setting?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
// Don't current support any nullable settings
|
||||
guard let updatedSetting: Setting = updated else { return }
|
||||
|
||||
|
@ -258,11 +283,12 @@ internal extension SessionUtil {
|
|||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userProfile,
|
||||
publicKey: userPublicKey
|
||||
) { conf in
|
||||
publicKey: userPublicKey,
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.updateSettings(
|
||||
checkForCommunityMessageRequests: updatedSetting.unsafeValue(as: Bool.self),
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -402,7 +428,8 @@ public extension SessionUtil {
|
|||
_ db: Database? = nil,
|
||||
threadId: String,
|
||||
threadVariant: SessionThread.Variant,
|
||||
visibleOnly: Bool
|
||||
visibleOnly: Bool,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Bool {
|
||||
let userPublicKey: String = getUserHexEncodedPublicKey(db)
|
||||
let configVariant: ConfigDump.Variant = {
|
||||
|
@ -412,10 +439,12 @@ public extension SessionUtil {
|
|||
}
|
||||
}()
|
||||
|
||||
return SessionUtil
|
||||
return dependencies.caches[.sessionUtil]
|
||||
.config(for: configVariant, publicKey: userPublicKey)
|
||||
.wrappedValue
|
||||
.map { conf in
|
||||
.map { config in
|
||||
guard case .object(let conf) = config else { return false }
|
||||
|
||||
var cThreadId: [CChar] = threadId.cArray.nullTerminated()
|
||||
|
||||
switch threadVariant {
|
||||
|
|
|
@ -16,13 +16,13 @@ internal extension SessionUtil {
|
|||
displayPictureUrl: String?,
|
||||
displayPictureFilename: String?,
|
||||
displayPictureEncryptionKey: Data?,
|
||||
members: Set<String>,
|
||||
admins: Set<String>,
|
||||
members: [(id: String, profile: Profile?)],
|
||||
admins: [(id: String, profile: Profile?)],
|
||||
using dependencies: Dependencies
|
||||
) throws -> (identityKeyPair: KeyPair, group: ClosedGroup, members: [GroupMember]) {
|
||||
guard
|
||||
let groupIdentityKeyPair: KeyPair = dependencies.crypto.generate(.ed25519KeyPair()),
|
||||
let userED25519KeyPair: KeyPair = Identity.fetchUserEd25519KeyPair(db)
|
||||
let userED25519KeyPair: KeyPair = Identity.fetchUserEd25519KeyPair(db, using: dependencies)
|
||||
else { throw MessageSenderError.noKeyPair }
|
||||
|
||||
// There will probably be custom init functions, will need a way to save the conf into
|
||||
|
@ -30,9 +30,12 @@ internal extension SessionUtil {
|
|||
var secretKey: [UInt8] = userED25519KeyPair.secretKey
|
||||
var groupIdentityPublicKey: [UInt8] = groupIdentityKeyPair.publicKey
|
||||
var groupIdentityPrivateKey: [UInt8] = groupIdentityKeyPair.secretKey
|
||||
let groupIdentityPublicKeyString: String = groupIdentityKeyPair.publicKey.toHexString()
|
||||
let creationTimestamp: TimeInterval = TimeInterval(SnodeAPI.currentOffsetTimestampMs() * 1000)
|
||||
let groupId: SessionId = SessionId(.group, publicKey: groupIdentityKeyPair.publicKey)
|
||||
let creationTimestamp: TimeInterval = TimeInterval(
|
||||
SnodeAPI.currentOffsetTimestampMs(using: dependencies) / 1000
|
||||
)
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let currentUserProfile: Profile? = Profile.fetchOrCreateCurrentUser(db, using: dependencies)
|
||||
|
||||
// Create the new config objects
|
||||
var groupKeysConf: UnsafeMutablePointer<config_group_keys>? = nil
|
||||
|
@ -55,38 +58,111 @@ internal extension SessionUtil {
|
|||
0,
|
||||
&error
|
||||
).orThrow(error: error)
|
||||
try groups_keys_init(
|
||||
&groupKeysConf,
|
||||
&secretKey,
|
||||
&groupIdentityPublicKey,
|
||||
&groupIdentityPrivateKey,
|
||||
groupInfoConf,
|
||||
groupMembersConf,
|
||||
nil,
|
||||
0,
|
||||
&error
|
||||
).orThrow(error: error)
|
||||
|
||||
guard
|
||||
let keysConf: UnsafeMutablePointer<config_group_keys> = groupKeysConf,
|
||||
let infoConf: UnsafeMutablePointer<config_object> = groupInfoConf,
|
||||
let membersConf: UnsafeMutablePointer<config_object> = groupMembersConf
|
||||
else {
|
||||
SNLog("[SessionUtil Error] Group config objects were null")
|
||||
throw SessionUtilError.unableToCreateConfigObject
|
||||
}
|
||||
|
||||
// Set the initial values in the confs
|
||||
var groupName: [CChar] = name.cArray.nullTerminated()
|
||||
groups_info_set_name(groupInfoConf, &groupName)
|
||||
groups_info_set_created(groupInfoConf, Int64(floor(creationTimestamp)))
|
||||
|
||||
if var displayPictureUrl: String = displayPictureUrl, var displayPictureEncryptionKey: Data = displayPictureEncryptionKey {
|
||||
if
|
||||
let displayPictureUrl: String = displayPictureUrl,
|
||||
let displayPictureEncryptionKey: Data = displayPictureEncryptionKey
|
||||
{
|
||||
var displayPic: user_profile_pic = user_profile_pic()
|
||||
displayPic.url = displayPictureUrl.toLibSession()
|
||||
displayPic.key = displayPictureEncryptionKey.toLibSession()
|
||||
groups_info_set_pic(groupInfoConf, displayPic)
|
||||
}
|
||||
|
||||
// Create dumps for the configs
|
||||
try [.groupKeys, .groupInfo, .groupMembers].forEach { variant in
|
||||
try SessionUtil.pushChangesIfNeeded(db, for: variant, publicKey: groupIdentityPublicKeyString)
|
||||
// Store the members/admins in the group (reduce to ensure we don't accidentally insert duplicates)
|
||||
let finalMembers: [String: (profile: Profile?, isAdmin: Bool)] = members
|
||||
.map { ($0.id, $0.profile, false) }
|
||||
.appending(contentsOf: admins.map { ($0.id, $0.profile, true) })
|
||||
.appending((currentUserPublicKey, currentUserProfile, true))
|
||||
.reduce(into: [:]) { result, next in result[next.0] = (profile: next.1, isAdmin: next.2)}
|
||||
|
||||
try finalMembers.forEach { memberId, info in
|
||||
var profilePic: user_profile_pic = user_profile_pic()
|
||||
|
||||
if
|
||||
let picUrl: String = info.profile?.profilePictureUrl,
|
||||
let picKey: Data = info.profile?.profileEncryptionKey
|
||||
{
|
||||
profilePic.url = picUrl.toLibSession()
|
||||
profilePic.key = picKey.toLibSession()
|
||||
}
|
||||
|
||||
try CExceptionHelper.performSafely {
|
||||
var member: config_group_member = config_group_member(
|
||||
session_id: memberId.toLibSession(),
|
||||
name: (info.profile?.name ?? "").toLibSession(),
|
||||
profile_pic: profilePic,
|
||||
admin: info.isAdmin,
|
||||
invited: 0,
|
||||
promoted: 0
|
||||
)
|
||||
|
||||
groups_members_set(membersConf, &member)
|
||||
}
|
||||
}
|
||||
// Load them into memory
|
||||
let groupState: [ConfigDump.Variant: Config] = [
|
||||
.groupKeys: .groupKeys(keysConf, info: infoConf, members: membersConf),
|
||||
.groupInfo: .object(infoConf),
|
||||
.groupMembers: .object(membersConf),
|
||||
]
|
||||
dependencies.caches.mutate(cache: .sessionUtil) { cache in
|
||||
groupState.forEach { variant, config in
|
||||
cache.setConfig(for: variant, publicKey: groupId.hexString, to: config)
|
||||
}
|
||||
}
|
||||
|
||||
// Create and save dumps for the configs
|
||||
try groupState.forEach { variant, config in
|
||||
try SessionUtil.createDump(
|
||||
config: config,
|
||||
for: variant,
|
||||
publicKey: groupId.hexString,
|
||||
timestampMs: Int64(floor(creationTimestamp * 1000))
|
||||
)?.save(db)
|
||||
}
|
||||
|
||||
// Add the new group to the USER_GROUPS config message
|
||||
try SessionUtil.add(
|
||||
db,
|
||||
groupIdentityPublicKey: groupIdentityPublicKeyString,
|
||||
groupIdentityPublicKey: groupId.hexString,
|
||||
groupIdentityPrivateKey: Data(groupIdentityPrivateKey),
|
||||
name: name,
|
||||
tag: nil,
|
||||
subkey: nil
|
||||
subkey: nil,
|
||||
joinedAt: Int64(floor(creationTimestamp)),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
return (
|
||||
groupIdentityKeyPair,
|
||||
ClosedGroup(
|
||||
threadId: groupIdentityPublicKeyString,
|
||||
threadId: groupId.hexString,
|
||||
name: name,
|
||||
formationTimestamp: creationTimestamp,
|
||||
displayPictureUrl: displayPictureUrl,
|
||||
|
@ -96,25 +172,14 @@ internal extension SessionUtil {
|
|||
groupIdentityPrivateKey: Data(groupIdentityPrivateKey),
|
||||
approved: true
|
||||
),
|
||||
members
|
||||
.map { memberId -> GroupMember in
|
||||
GroupMember(
|
||||
groupId: groupIdentityPublicKeyString,
|
||||
profileId: memberId,
|
||||
role: .standard,
|
||||
isHidden: false
|
||||
)
|
||||
}
|
||||
.appending(
|
||||
contentsOf: admins.map { memberId -> GroupMember in
|
||||
GroupMember(
|
||||
groupId: groupIdentityPublicKeyString,
|
||||
profileId: memberId,
|
||||
role: .admin,
|
||||
isHidden: false
|
||||
)
|
||||
}
|
||||
finalMembers.map { memberId, info -> GroupMember in
|
||||
GroupMember(
|
||||
groupId: groupId.hexString,
|
||||
profileId: memberId,
|
||||
role: (info.isAdmin ? .admin : .standard),
|
||||
isHidden: false
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -131,6 +196,7 @@ internal extension SessionUtil {
|
|||
) throws -> (group: ClosedGroup, members: [GroupMember]) {
|
||||
// TODO: This!!!
|
||||
preconditionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
private extension Int32 {
|
||||
|
|
|
@ -25,19 +25,19 @@ internal extension SessionUtil {
|
|||
|
||||
// MARK: - Incoming Changes
|
||||
|
||||
static func handleGroupsUpdate(
|
||||
static func handleUserGroupsUpdate(
|
||||
_ db: Database,
|
||||
in conf: UnsafeMutablePointer<config_object>?,
|
||||
mergeNeedsDump: Bool,
|
||||
in config: Config?,
|
||||
latestConfigSentTimestampMs: Int64,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard mergeNeedsDump else { return }
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard config.needsDump else { return }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
var infiniteLoopGuard: Int = 0
|
||||
var communities: [PrioritisedData<OpenGroupUrlInfo>] = []
|
||||
var legacyGroups: [LegacyGroupInfo] = []
|
||||
var groups: [GroupInfo] = []
|
||||
var community: ugroups_community_info = ugroups_community_info()
|
||||
var legacyGroup: ugroups_legacy_group_info = ugroups_legacy_group_info()
|
||||
let groupsIterator: OpaquePointer = user_groups_iterator_new(conf)
|
||||
|
@ -193,7 +193,8 @@ internal extension SessionUtil {
|
|||
threadIds: Array(communityIdsToRemove),
|
||||
threadVariant: .community,
|
||||
groupLeaveType: .forced,
|
||||
calledFromConfigHandling: true
|
||||
calledFromConfigHandling: true,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -223,7 +224,7 @@ internal extension SessionUtil {
|
|||
|
||||
if !existingLegacyGroupIds.contains(group.id) {
|
||||
// Add a new group if it doesn't already exist
|
||||
try MessageReceiver.handleNewClosedGroup(
|
||||
try MessageReceiver.handleNewLegacyClosedGroup(
|
||||
db,
|
||||
groupPublicKey: group.id,
|
||||
name: name,
|
||||
|
@ -380,7 +381,8 @@ internal extension SessionUtil {
|
|||
threadIds: Array(legacyGroupIdsToRemove),
|
||||
threadVariant: .legacyGroup,
|
||||
groupLeaveType: .forced,
|
||||
calledFromConfigHandling: true
|
||||
calledFromConfigHandling: true,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -409,9 +411,9 @@ internal extension SessionUtil {
|
|||
|
||||
static func upsert(
|
||||
legacyGroups: [LegacyGroupInfo],
|
||||
in conf: UnsafeMutablePointer<config_object>?
|
||||
in config: Config?
|
||||
) throws {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
guard !legacyGroups.isEmpty else { return }
|
||||
|
||||
try legacyGroups
|
||||
|
@ -420,7 +422,7 @@ internal extension SessionUtil {
|
|||
guard let userGroup: UnsafeMutablePointer<ugroups_legacy_group_info> = user_groups_get_or_construct_legacy_group(conf, &cGroupId) else {
|
||||
/// It looks like there are some situations where this object might not get created correctly (and
|
||||
/// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead
|
||||
SNLog("Unable to upsert legacy group conversation to SessionUtil: \(SessionUtil.lastError(conf))")
|
||||
SNLog("Unable to upsert legacy group conversation to SessionUtil: \(config.lastError)")
|
||||
throw SessionUtilError.getOrConstructFailedUnexpectedly
|
||||
}
|
||||
|
||||
|
@ -515,10 +517,19 @@ internal extension SessionUtil {
|
|||
}
|
||||
|
||||
static func upsert(
|
||||
communities: [CommunityInfo],
|
||||
in conf: UnsafeMutablePointer<config_object>?
|
||||
groups: [GroupInfo],
|
||||
in config: Config?
|
||||
) throws {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
guard !groups.isEmpty else { return }
|
||||
|
||||
}
|
||||
|
||||
static func upsert(
|
||||
communities: [CommunityInfo],
|
||||
in config: Config?
|
||||
) throws {
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
guard !communities.isEmpty else { return }
|
||||
|
||||
try communities
|
||||
|
@ -531,7 +542,7 @@ internal extension SessionUtil {
|
|||
guard user_groups_get_or_construct_community(conf, &userCommunity, &cBaseUrl, &cRoom, &cPubkey) else {
|
||||
/// It looks like there are some situations where this object might not get created correctly (and
|
||||
/// will throw due to the implicit unwrapping) as a result we put it in a guard and throw instead
|
||||
SNLog("Unable to upsert community conversation to SessionUtil: \(SessionUtil.lastError(conf))")
|
||||
SNLog("Unable to upsert community conversation to SessionUtil: \(config.lastError)")
|
||||
throw SessionUtilError.getOrConstructFailedUnexpectedly
|
||||
}
|
||||
|
||||
|
@ -551,13 +562,15 @@ public extension SessionUtil {
|
|||
_ db: Database,
|
||||
server: String,
|
||||
rootToken: String,
|
||||
publicKey: String
|
||||
publicKey: String,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.upsert(
|
||||
communities: [
|
||||
CommunityInfo(
|
||||
|
@ -569,17 +582,25 @@ public extension SessionUtil {
|
|||
)
|
||||
)
|
||||
],
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static func remove(_ db: Database, server: String, roomToken: String) throws {
|
||||
static func remove(
|
||||
_ db: Database,
|
||||
server: String,
|
||||
roomToken: String,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
var cBaseUrl: [CChar] = server.cArray.nullTerminated()
|
||||
var cRoom: [CChar] = roomToken.cArray.nullTerminated()
|
||||
|
||||
|
@ -597,7 +618,8 @@ public extension SessionUtil {
|
|||
roomToken: roomToken,
|
||||
publicKey: ""
|
||||
)
|
||||
]
|
||||
],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -612,14 +634,17 @@ public extension SessionUtil {
|
|||
latestKeyPairReceivedTimestamp: TimeInterval,
|
||||
disappearingConfig: DisappearingMessagesConfiguration,
|
||||
members: Set<String>,
|
||||
admins: Set<String>
|
||||
admins: Set<String>,
|
||||
formationTimestamp: TimeInterval,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
var cGroupId: [CChar] = legacyGroupPublicKey.cArray.nullTerminated()
|
||||
let userGroup: UnsafeMutablePointer<ugroups_legacy_group_info>? = user_groups_get_legacy_group(conf, &cGroupId)
|
||||
|
@ -661,10 +686,11 @@ public extension SessionUtil {
|
|||
role: .admin,
|
||||
isHidden: false
|
||||
)
|
||||
}
|
||||
},
|
||||
joinedAt: Int64(floor(formationTimestamp))
|
||||
)
|
||||
],
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -676,13 +702,15 @@ public extension SessionUtil {
|
|||
latestKeyPair: ClosedGroupKeyPair? = nil,
|
||||
disappearingConfig: DisappearingMessagesConfiguration? = nil,
|
||||
members: Set<String>? = nil,
|
||||
admins: Set<String>? = nil
|
||||
admins: Set<String>? = nil,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.upsert(
|
||||
legacyGroups: [
|
||||
LegacyGroupInfo(
|
||||
|
@ -710,20 +738,22 @@ public extension SessionUtil {
|
|||
}
|
||||
)
|
||||
],
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static func batchUpdate(
|
||||
_ db: Database,
|
||||
disappearingConfigs: [DisappearingMessagesConfiguration]
|
||||
disappearingConfigs: [DisappearingMessagesConfiguration],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.upsert(
|
||||
legacyGroups: disappearingConfigs.map {
|
||||
LegacyGroupInfo(
|
||||
|
@ -731,19 +761,26 @@ public extension SessionUtil {
|
|||
disappearingConfig: $0
|
||||
)
|
||||
},
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static func remove(_ db: Database, legacyGroupIds: [String]) throws {
|
||||
static func remove(
|
||||
_ db: Database,
|
||||
legacyGroupIds: [String],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard !legacyGroupIds.isEmpty else { return }
|
||||
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
legacyGroupIds.forEach { threadId in
|
||||
var cGroupId: [CChar] = threadId.cArray.nullTerminated()
|
||||
|
||||
|
@ -753,7 +790,7 @@ public extension SessionUtil {
|
|||
}
|
||||
|
||||
// Remove the volatile info as well
|
||||
try SessionUtil.remove(db, volatileLegacyGroupIds: legacyGroupIds)
|
||||
try SessionUtil.remove(db, volatileLegacyGroupIds: legacyGroupIds, using: dependencies)
|
||||
}
|
||||
|
||||
// MARK: -- Group Changes
|
||||
|
@ -764,15 +801,17 @@ public extension SessionUtil {
|
|||
groupIdentityPrivateKey: Data?,
|
||||
name: String,
|
||||
tag: Data?,
|
||||
subkey: Data?
|
||||
subkey: Data?,
|
||||
joinedAt: Int64,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
var cGroupId: [CChar] = groupIdentityPublicKey.cArray.nullTerminated()
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -782,13 +821,15 @@ public extension SessionUtil {
|
|||
groupIdentityPrivateKey: Data? = nil,
|
||||
name: String? = nil,
|
||||
tag: Data? = nil,
|
||||
subkey: Data? = nil
|
||||
subkey: Data? = nil,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db)
|
||||
) { conf in
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
try SessionUtil.upsert(
|
||||
groups: [
|
||||
GroupInfo(
|
||||
|
@ -799,14 +840,28 @@ public extension SessionUtil {
|
|||
subkey: subkey
|
||||
)
|
||||
],
|
||||
in: conf
|
||||
in: config
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static func remove(_ db: Database, groupIds: [String]) throws {
|
||||
static func remove(
|
||||
_ db: Database,
|
||||
groupIds: [String],
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
guard !groupIds.isEmpty else { return }
|
||||
|
||||
try SessionUtil.performAndPushChange(
|
||||
db,
|
||||
for: .userGroups,
|
||||
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
|
||||
using: dependencies
|
||||
) { config in
|
||||
}
|
||||
|
||||
// Remove the volatile info as well
|
||||
try SessionUtil.remove(db, volatileGroupIds: groupIds, using: dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,15 +20,14 @@ internal extension SessionUtil {
|
|||
|
||||
static func handleUserProfileUpdate(
|
||||
_ db: Database,
|
||||
in conf: UnsafeMutablePointer<config_object>?,
|
||||
mergeNeedsDump: Bool,
|
||||
in config: Config?,
|
||||
latestConfigSentTimestampMs: Int64,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
typealias ProfileData = (profileName: String, profilePictureUrl: String?, profilePictureKey: Data?)
|
||||
|
||||
guard mergeNeedsDump else { return }
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard config.needsDump else { return }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
// A profile must have a name so if this is null then it's invalid and can be ignored
|
||||
guard let profileNamePtr: UnsafePointer<CChar> = user_profile_get_name(conf) else { return }
|
||||
|
@ -114,7 +113,8 @@ internal extension SessionUtil {
|
|||
threadId: userPublicKey,
|
||||
threadVariant: .contact,
|
||||
groupLeaveType: .forced,
|
||||
calledFromConfigHandling: true
|
||||
calledFromConfigHandling: true,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -178,9 +178,9 @@ internal extension SessionUtil {
|
|||
|
||||
static func update(
|
||||
profile: Profile,
|
||||
in conf: UnsafeMutablePointer<config_object>?
|
||||
in config: Config?
|
||||
) throws {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
// Update the name
|
||||
var updatedName: [CChar] = profile.name.cArray.nullTerminated()
|
||||
|
@ -196,9 +196,9 @@ internal extension SessionUtil {
|
|||
static func updateNoteToSelf(
|
||||
priority: Int32? = nil,
|
||||
disappearingMessagesConfig: DisappearingMessagesConfiguration? = nil,
|
||||
in conf: UnsafeMutablePointer<config_object>?
|
||||
in config: Config?
|
||||
) throws {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
if let priority: Int32 = priority {
|
||||
user_profile_set_nts_priority(conf, priority)
|
||||
|
@ -211,9 +211,9 @@ internal extension SessionUtil {
|
|||
|
||||
static func updateSettings(
|
||||
checkForCommunityMessageRequests: Bool? = nil,
|
||||
in conf: UnsafeMutablePointer<config_object>?
|
||||
in config: Config?
|
||||
) throws {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
if let blindedMessageRequests: Bool = checkForCommunityMessageRequests {
|
||||
user_profile_set_blinded_msgreqs(conf, (blindedMessageRequests ? 1 : 0))
|
||||
|
@ -224,8 +224,8 @@ internal extension SessionUtil {
|
|||
// MARK: - Direct Values
|
||||
|
||||
extension SessionUtil {
|
||||
static func rawBlindedMessageRequestValue(in conf: UnsafeMutablePointer<config_object>?) throws -> Int32 {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
static func rawBlindedMessageRequestValue(in config: Config?) throws -> Int32 {
|
||||
guard case .object(let conf) = config else { throw SessionUtilError.invalidConfigObject }
|
||||
|
||||
return user_profile_get_blinded_msgreqs(conf)
|
||||
}
|
||||
|
|
|
@ -52,15 +52,17 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
|
|||
@discardableResult
|
||||
func updateAllAndConfig(
|
||||
_ db: Database,
|
||||
_ assignments: ConfigColumnAssignment...
|
||||
_ assignments: ConfigColumnAssignment...,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> Int {
|
||||
return try updateAllAndConfig(db, assignments)
|
||||
return try updateAllAndConfig(db, assignments, using: dependencies)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func updateAllAndConfig(
|
||||
_ db: Database,
|
||||
_ assignments: [ConfigColumnAssignment]
|
||||
_ assignments: [ConfigColumnAssignment],
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> Int {
|
||||
let targetAssignments: [ColumnAssignment] = assignments.map { $0.assignment }
|
||||
|
||||
|
@ -69,7 +71,7 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
|
|||
return try self.updateAll(db, targetAssignments)
|
||||
}
|
||||
|
||||
return try self.updateAndFetchAllAndUpdateConfig(db, assignments).count
|
||||
return try self.updateAndFetchAllAndUpdateConfig(db, assignments, using: dependencies).count
|
||||
}
|
||||
|
||||
// MARK: -- updateAndFetchAll
|
||||
|
@ -77,15 +79,17 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
|
|||
@discardableResult
|
||||
func updateAndFetchAllAndUpdateConfig(
|
||||
_ db: Database,
|
||||
_ assignments: ConfigColumnAssignment...
|
||||
_ assignments: ConfigColumnAssignment...,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> [RowDecoder] {
|
||||
return try updateAndFetchAllAndUpdateConfig(db, assignments)
|
||||
return try updateAndFetchAllAndUpdateConfig(db, assignments, using: dependencies)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func updateAndFetchAllAndUpdateConfig(
|
||||
_ db: Database,
|
||||
_ assignments: [ConfigColumnAssignment]
|
||||
_ assignments: [ConfigColumnAssignment],
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws -> [RowDecoder] {
|
||||
// First perform the actual updates
|
||||
let updatedData: [RowDecoder] = try self.updateAndFetchAll(db, assignments.map { $0.assignment })
|
||||
|
@ -107,20 +111,20 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
|
|||
// Update the config dump state where needed
|
||||
switch self {
|
||||
case is QueryInterfaceRequest<Contact>:
|
||||
return try SessionUtil.updatingContacts(db, updatedData)
|
||||
return try SessionUtil.updatingContacts(db, updatedData, using: dependencies)
|
||||
|
||||
case is QueryInterfaceRequest<Profile>:
|
||||
return try SessionUtil.updatingProfiles(db, updatedData)
|
||||
return try SessionUtil.updatingProfiles(db, updatedData, using: dependencies)
|
||||
|
||||
case is QueryInterfaceRequest<SessionThread>:
|
||||
return try SessionUtil.updatingThreads(db, updatedData)
|
||||
return try SessionUtil.updatingThreads(db, updatedData, using: dependencies)
|
||||
|
||||
case is QueryInterfaceRequest<ClosedGroup>:
|
||||
return try SessionUtil.updatingGroupInfo(db, updatedData)
|
||||
return try SessionUtil.updatingGroupInfo(db, updatedData, using: dependencies)
|
||||
|
||||
case is QueryInterfaceRequest<DisappearingMessagesConfiguration>:
|
||||
let oneToOneUpdates: [RowDecoder] = try SessionUtil.updatingDisappearingConfigsOneToOne(db, updatedData)
|
||||
let groupUpdates: [RowDecoder] = try SessionUtil.updatingDisappearingConfigsGroups(db, updatedData)
|
||||
let oneToOneUpdates: [RowDecoder] = try SessionUtil.updatingDisappearingConfigsOneToOne(db, updatedData, using: dependencies)
|
||||
let groupUpdates: [RowDecoder] = try SessionUtil.updatingDisappearingConfigsGroups(db, updatedData, using: dependencies)
|
||||
|
||||
return (oneToOneUpdates + groupUpdates)
|
||||
|
||||
|
|
|
@ -5,39 +5,103 @@ import GRDB
|
|||
import SessionUtilitiesKit
|
||||
|
||||
public extension Database {
|
||||
func setAndUpdateConfig(_ key: Setting.BoolKey, to newValue: Bool) throws {
|
||||
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
|
||||
func setAndUpdateConfig(
|
||||
_ key: Setting.BoolKey,
|
||||
to newValue: Bool,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try updateConfigIfNeeded(
|
||||
self,
|
||||
key: key.rawValue,
|
||||
updatedSetting: self.setting(key: key, to: newValue),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
func setAndUpdateConfig(_ key: Setting.DoubleKey, to newValue: Double?) throws {
|
||||
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
|
||||
func setAndUpdateConfig(
|
||||
_ key: Setting.DoubleKey,
|
||||
to newValue: Double?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try updateConfigIfNeeded(
|
||||
self,
|
||||
key: key.rawValue,
|
||||
updatedSetting: self.setting(key: key, to: newValue),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
func setAndUpdateConfig(_ key: Setting.IntKey, to newValue: Int?) throws {
|
||||
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
|
||||
func setAndUpdateConfig(
|
||||
_ key: Setting.IntKey,
|
||||
to newValue: Int?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try updateConfigIfNeeded(
|
||||
self,
|
||||
key: key.rawValue,
|
||||
updatedSetting: self.setting(key: key, to: newValue),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
func setAndUpdateConfig(_ key: Setting.StringKey, to newValue: String?) throws {
|
||||
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
|
||||
func setAndUpdateConfig(
|
||||
_ key: Setting.StringKey,
|
||||
to newValue: String?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try updateConfigIfNeeded(
|
||||
self,
|
||||
key: key.rawValue,
|
||||
updatedSetting: self.setting(key: key, to: newValue),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
func setAndUpdateConfig<T: EnumIntSetting>(_ key: Setting.EnumKey, to newValue: T?) throws {
|
||||
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
|
||||
func setAndUpdateConfig<T: EnumIntSetting>(
|
||||
_ key: Setting.EnumKey,
|
||||
to newValue: T?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try updateConfigIfNeeded(
|
||||
self,
|
||||
key: key.rawValue,
|
||||
updatedSetting: self.setting(key: key, to: newValue),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
func setAndUpdateConfig<T: EnumStringSetting>(_ key: Setting.EnumKey, to newValue: T?) throws {
|
||||
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
|
||||
func setAndUpdateConfig<T: EnumStringSetting>(
|
||||
_ key: Setting.EnumKey,
|
||||
to newValue: T?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try updateConfigIfNeeded(
|
||||
self,
|
||||
key: key.rawValue,
|
||||
updatedSetting: self.setting(key: key, to: newValue),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
/// Value will be stored as a timestamp in seconds since 1970
|
||||
func setAndUpdateConfig(_ key: Setting.DateKey, to newValue: Date?) throws {
|
||||
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
|
||||
func setAndUpdateConfig(
|
||||
_ key: Setting.DateKey,
|
||||
to newValue: Date?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
try updateConfigIfNeeded(
|
||||
self,
|
||||
key: key.rawValue,
|
||||
updatedSetting: self.setting(key: key, to: newValue),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
private func updateConfigIfNeeded(
|
||||
_ db: Database,
|
||||
key: String,
|
||||
updatedSetting: Setting?
|
||||
updatedSetting: Setting?,
|
||||
using dependencies: Dependencies
|
||||
) throws {
|
||||
// Before we do anything custom make sure the setting should trigger a change
|
||||
guard SessionUtil.syncedSettings.contains(key) else { return }
|
||||
|
@ -53,6 +117,6 @@ public extension Database {
|
|||
}
|
||||
}
|
||||
|
||||
try SessionUtil.updatingSetting(db, updatedSetting)
|
||||
try SessionUtil.updatingSetting(db, updatedSetting, using: dependencies)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,62 +29,35 @@ public enum SessionUtil {
|
|||
let obsoleteHashes: [String]
|
||||
}
|
||||
|
||||
// MARK: - Configs
|
||||
|
||||
fileprivate static var configStore: Atomic<[ConfigKey: Atomic<UnsafeMutablePointer<config_object>?>]> = Atomic([:])
|
||||
|
||||
public static func config(for variant: ConfigDump.Variant, publicKey: String) -> Atomic<UnsafeMutablePointer<config_object>?> {
|
||||
let key: ConfigKey = ConfigKey(variant: variant, publicKey: publicKey)
|
||||
|
||||
return (
|
||||
SessionUtil.configStore.wrappedValue[key] ??
|
||||
Atomic(nil)
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Variables
|
||||
|
||||
internal static func syncDedupeId(_ publicKey: String) -> String {
|
||||
return "EnqueueConfigurationSyncJob-\(publicKey)"
|
||||
}
|
||||
|
||||
/// Returns `true` if there is a config which needs to be pushed, but returns `false` if the configs are all up to date or haven't been
|
||||
/// loaded yet (eg. fresh install)
|
||||
public static var needsSync: Bool {
|
||||
configStore
|
||||
.wrappedValue
|
||||
.contains { _, atomicConf in
|
||||
guard atomicConf.wrappedValue != nil else { return false }
|
||||
|
||||
return config_needs_push(atomicConf.wrappedValue)
|
||||
}
|
||||
}
|
||||
|
||||
public static var libSessionVersion: String { String(cString: LIBSESSION_UTIL_VERSION_STR) }
|
||||
|
||||
internal static func lastError(_ conf: UnsafeMutablePointer<config_object>?) -> String {
|
||||
return (conf?.pointee.last_error.map { String(cString: $0) } ?? "Unknown")
|
||||
}
|
||||
|
||||
// MARK: - Loading
|
||||
|
||||
public static func clearMemoryState() {
|
||||
SessionUtil.configStore.mutate { confStore in
|
||||
confStore.removeAll()
|
||||
public static func clearMemoryState(using dependencies: Dependencies) {
|
||||
dependencies.caches.mutate(cache: .sessionUtil) { cache in
|
||||
cache.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
public static func loadState(_ db: Database, using dependencies: Dependencies = Dependencies()) {
|
||||
public static func loadState(_ db: Database, using dependencies: Dependencies) {
|
||||
// Ensure we have the ed25519 key and that we haven't already loaded the state before
|
||||
// we continue
|
||||
guard
|
||||
let ed25519SecretKey: [UInt8] = Identity.fetchUserEd25519KeyPair(db)?.secretKey,
|
||||
SessionUtil.configStore.wrappedValue.isEmpty
|
||||
dependencies.caches[.sessionUtil].isEmpty
|
||||
else { return }
|
||||
|
||||
// Retrieve the existing dumps from the database
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
|
||||
let existingDumps: Set<ConfigDump> = ((try? ConfigDump.fetchSet(db)) ?? [])
|
||||
.sorted { lhs, rhs in lhs.variant.processingOrder < rhs.variant.processingOrder }
|
||||
.asSet()
|
||||
let existingDumpVariants: Set<ConfigDump.Variant> = existingDumps
|
||||
.map { $0.variant }
|
||||
.asSet()
|
||||
|
@ -98,53 +71,51 @@ public enum SessionUtil {
|
|||
.defaulting(to: [:])
|
||||
|
||||
// Create the 'config_object' records for each dump
|
||||
SessionUtil.configStore.mutate { confStore in
|
||||
dependencies.caches.mutate(cache: .sessionUtil) { cache in
|
||||
existingDumps.forEach { dump in
|
||||
confStore[ConfigKey(variant: dump.variant, publicKey: dump.publicKey)] = Atomic(
|
||||
try? SessionUtil.loadState(
|
||||
cache.setConfig(
|
||||
for: dump.variant,
|
||||
publicKey: dump.publicKey,
|
||||
to: try? SessionUtil.loadState(
|
||||
for: dump.variant,
|
||||
publicKey: dump.publicKey,
|
||||
userEd25519SecretKey: ed25519SecretKey,
|
||||
groupEd25519SecretKey: groupsByKey[dump.publicKey].map { Array($0) },
|
||||
cachedData: dump.data
|
||||
cachedData: dump.data,
|
||||
using: dependencies
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
missingRequiredVariants.forEach { variant in
|
||||
confStore[ConfigKey(variant: variant, publicKey: currentUserPublicKey)] = Atomic(
|
||||
try? SessionUtil.loadState(
|
||||
cache.setConfig(
|
||||
for: variant,
|
||||
publicKey: currentUserPublicKey,
|
||||
to: try? SessionUtil.loadState(
|
||||
for: variant,
|
||||
publicKey: currentUserPublicKey,
|
||||
userEd25519SecretKey: ed25519SecretKey,
|
||||
groupEd25519SecretKey: nil,
|
||||
cachedData: nil
|
||||
cachedData: nil,
|
||||
using: dependencies
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func storeState(
|
||||
_ configs: [ConfigDump.Variant: UnsafeMutablePointer<config_object>?],
|
||||
publicKey: String
|
||||
) {
|
||||
SessionUtil.configStore.mutate { confStore in
|
||||
configs.forEach { variant, conf in
|
||||
confStore[ConfigKey(variant: variant, publicKey: publicKey)] = Atomic(conf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static func loadState(
|
||||
for variant: ConfigDump.Variant,
|
||||
publicKey: String,
|
||||
userEd25519SecretKey: [UInt8],
|
||||
groupEd25519SecretKey: [UInt8]?,
|
||||
cachedData: Data?
|
||||
) throws -> UnsafeMutablePointer<config_object>? {
|
||||
cachedData: Data?,
|
||||
using dependencies: Dependencies
|
||||
) throws -> Config {
|
||||
// Setup initial variables (including getting the memory address for any cached data)
|
||||
var conf: UnsafeMutablePointer<config_object>? = nil
|
||||
var keysConf: UnsafeMutablePointer<config_group_keys>? = nil
|
||||
var secretKey: [UInt8]? = userEd25519SecretKey
|
||||
var error: [CChar] = [CChar](repeating: 0, count: 256)
|
||||
let cachedDump: (data: UnsafePointer<UInt8>, length: Int)? = cachedData?.withUnsafeBytes { unsafeBytes in
|
||||
return unsafeBytes.baseAddress.map {
|
||||
|
@ -156,61 +127,143 @@ public enum SessionUtil {
|
|||
}
|
||||
|
||||
// Try to create the object
|
||||
var secretKey: [UInt8]? = userEd25519SecretKey
|
||||
let result: Int32 = {
|
||||
return try {
|
||||
switch variant {
|
||||
case .userProfile:
|
||||
return user_profile_init(&conf, &secretKey, cachedDump?.data, (cachedDump?.length ?? 0), &error)
|
||||
return try user_profile_init(
|
||||
&conf,
|
||||
&secretKey,
|
||||
cachedDump?.data,
|
||||
(cachedDump?.length ?? 0),
|
||||
&error
|
||||
)
|
||||
.returning(
|
||||
Config.from(conf),
|
||||
orThrow: "Unable to create \(variant.rawValue) config object",
|
||||
error: error
|
||||
)
|
||||
|
||||
case .contacts:
|
||||
return contacts_init(&conf, &secretKey, cachedDump?.data, (cachedDump?.length ?? 0), &error)
|
||||
return try contacts_init(
|
||||
&conf,
|
||||
&secretKey,
|
||||
cachedDump?.data,
|
||||
(cachedDump?.length ?? 0),
|
||||
&error
|
||||
)
|
||||
.returning(
|
||||
Config.from(conf),
|
||||
orThrow: "Unable to create \(variant.rawValue) config object",
|
||||
error: error
|
||||
)
|
||||
|
||||
case .convoInfoVolatile:
|
||||
return convo_info_volatile_init(&conf, &secretKey, cachedDump?.data, (cachedDump?.length ?? 0), &error)
|
||||
return try convo_info_volatile_init(
|
||||
&conf,
|
||||
&secretKey,
|
||||
cachedDump?.data,
|
||||
(cachedDump?.length ?? 0),
|
||||
&error
|
||||
)
|
||||
.returning(
|
||||
Config.from(conf),
|
||||
orThrow: "Unable to create \(variant.rawValue) config object",
|
||||
error: error
|
||||
)
|
||||
|
||||
case .userGroups:
|
||||
return user_groups_init(&conf, &secretKey, cachedDump?.data, (cachedDump?.length ?? 0), &error)
|
||||
|
||||
return try user_groups_init(
|
||||
&conf,
|
||||
&secretKey,
|
||||
cachedDump?.data,
|
||||
(cachedDump?.length ?? 0),
|
||||
&error
|
||||
)
|
||||
.returning(
|
||||
Config.from(conf),
|
||||
orThrow: "Unable to create \(variant.rawValue) config object",
|
||||
error: error
|
||||
)
|
||||
|
||||
case .groupInfo:
|
||||
return groups_info_init(&conf, &secretKey, &secretKey, cachedDump?.data, (cachedDump?.length ?? 0), &error)
|
||||
|
||||
return try groups_info_init(
|
||||
&conf,
|
||||
&secretKey,
|
||||
&secretKey,
|
||||
cachedDump?.data,
|
||||
(cachedDump?.length ?? 0),
|
||||
&error
|
||||
)
|
||||
.returning(
|
||||
Config.from(conf),
|
||||
orThrow: "Unable to create \(variant.rawValue) config object",
|
||||
error: error
|
||||
)
|
||||
|
||||
case .groupMembers:
|
||||
return groups_members_init(&conf, &secretKey, &secretKey, cachedDump?.data, (cachedDump?.length ?? 0), &error)
|
||||
|
||||
return try groups_members_init(
|
||||
&conf,
|
||||
&secretKey,
|
||||
&secretKey,
|
||||
cachedDump?.data,
|
||||
(cachedDump?.length ?? 0),
|
||||
&error
|
||||
)
|
||||
.returning(
|
||||
Config.from(conf),
|
||||
orThrow: "Unable to create \(variant.rawValue) config object",
|
||||
error: error
|
||||
)
|
||||
|
||||
case .groupKeys:
|
||||
preconditionFailure()
|
||||
var identityPublicKey: [UInt8] = Array(Data(hex: publicKey))
|
||||
var adminSecretKey: [UInt8]? = groupEd25519SecretKey
|
||||
let infoConfig: Config? = dependencies.caches[.sessionUtil]
|
||||
.config(for: .groupInfo, publicKey: publicKey)
|
||||
.wrappedValue
|
||||
let membersConfig: Config? = dependencies.caches[.sessionUtil]
|
||||
.config(for: .groupMembers, publicKey: publicKey)
|
||||
.wrappedValue
|
||||
|
||||
guard
|
||||
case .object(let infoConf) = infoConfig,
|
||||
case .object(let membersConf) = membersConfig
|
||||
else {
|
||||
SNLog("[SessionUtil Error] Unable to create \(variant.rawValue) config object: Group info and member config states not loaded")
|
||||
throw SessionUtilError.unableToCreateConfigObject
|
||||
}
|
||||
|
||||
return try groups_keys_init(
|
||||
&keysConf,
|
||||
&secretKey,
|
||||
&identityPublicKey,
|
||||
&adminSecretKey,
|
||||
infoConf,
|
||||
membersConf,
|
||||
cachedDump?.data,
|
||||
(cachedDump?.length ?? 0),
|
||||
&error
|
||||
)
|
||||
.returning(
|
||||
Config.from(keysConf, info: infoConf, members: membersConf),
|
||||
orThrow: "Unable to create \(variant.rawValue) config object",
|
||||
error: error
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
guard result == 0 else {
|
||||
SNLog("[SessionUtil Error] Unable to create \(variant.rawValue) config object: \(String(cString: error))")
|
||||
throw SessionUtilError.unableToCreateConfigObject
|
||||
}
|
||||
|
||||
return conf
|
||||
}
|
||||
|
||||
internal static func createDump(
|
||||
conf: UnsafeMutablePointer<config_object>?,
|
||||
config: Config?,
|
||||
for variant: ConfigDump.Variant,
|
||||
publicKey: String,
|
||||
timestampMs: Int64
|
||||
) throws -> ConfigDump? {
|
||||
guard conf != nil else { throw SessionUtilError.nilConfigObject }
|
||||
|
||||
// If it doesn't need a dump then do nothing
|
||||
guard config_needs_dump(conf) else { return nil }
|
||||
|
||||
var dumpResult: UnsafeMutablePointer<UInt8>? = nil
|
||||
var dumpResultLen: Int = 0
|
||||
try CExceptionHelper.performSafely {
|
||||
config_dump(conf, &dumpResult, &dumpResultLen)
|
||||
}
|
||||
|
||||
guard let dumpResult: UnsafeMutablePointer<UInt8> = dumpResult else { return nil }
|
||||
|
||||
let dumpData: Data = Data(bytes: dumpResult, count: dumpResultLen)
|
||||
dumpResult.deallocate()
|
||||
guard
|
||||
config.needsDump,
|
||||
let dumpData: Data = try config?.dump()
|
||||
else { return nil }
|
||||
|
||||
return ConfigDump(
|
||||
variant: variant,
|
||||
|
@ -224,7 +277,8 @@ public enum SessionUtil {
|
|||
|
||||
public static func pendingChanges(
|
||||
_ db: Database,
|
||||
publicKey: String
|
||||
publicKey: String,
|
||||
using dependencies: Dependencies
|
||||
) throws -> [OutgoingConfResult] {
|
||||
guard Identity.userExists(db) else { throw SessionUtilError.userDoesNotExist }
|
||||
|
||||
|
@ -245,59 +299,53 @@ public enum SessionUtil {
|
|||
// data yet (to deal with first launch cases)
|
||||
return try existingDumpVariants
|
||||
.compactMap { variant -> OutgoingConfResult? in
|
||||
try SessionUtil
|
||||
try dependencies.caches[.sessionUtil]
|
||||
.config(for: variant, publicKey: publicKey)
|
||||
.wrappedValue
|
||||
.map { conf in
|
||||
.map { config -> OutgoingConfResult? in
|
||||
// Check if the config needs to be pushed
|
||||
guard config_needs_push(conf) else { return nil }
|
||||
guard config.needsPush else { return nil }
|
||||
|
||||
var cPushData: UnsafeMutablePointer<config_push_data>!
|
||||
var result: (data: Data, seqNo: Int64, obsoleteHashes: [String])!
|
||||
let configCountInfo: String = {
|
||||
var result: String = "Invalid"
|
||||
|
||||
try? CExceptionHelper.performSafely {
|
||||
switch variant {
|
||||
case .userProfile: result = "1 profile"
|
||||
case .contacts: result = "\(contacts_size(conf)) contacts"
|
||||
case .userGroups: result = "\(user_groups_size(conf)) group conversations"
|
||||
case .convoInfoVolatile: result = "\(convo_info_volatile_size(conf)) volatile conversations"
|
||||
switch (config, variant) {
|
||||
case (_, .userProfile): result = "1 profile"
|
||||
case (.object(let conf), .contacts):
|
||||
result = "\(contacts_size(conf)) contacts"
|
||||
|
||||
case (.object(let conf), .userGroups):
|
||||
result = "\(user_groups_size(conf)) group conversations"
|
||||
|
||||
case (.object(let conf), .convoInfoVolatile):
|
||||
result = "\(convo_info_volatile_size(conf)) volatile conversations"
|
||||
|
||||
case (_, .groupInfo): result = "1 group info"
|
||||
case (.object(let conf), .groupMembers): result = ""
|
||||
case (_, .groupKeys): result = ""
|
||||
default: break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
do {
|
||||
try CExceptionHelper.performSafely {
|
||||
cPushData = config_push(conf)
|
||||
}
|
||||
}
|
||||
do { result = try config.push() }
|
||||
catch {
|
||||
SNLog("[libSession] Failed to generate push data for \(variant) config data, size: \(configCountInfo), error: \(error)")
|
||||
throw error
|
||||
}
|
||||
|
||||
let pushData: Data = Data(
|
||||
bytes: cPushData.pointee.config,
|
||||
count: cPushData.pointee.config_len
|
||||
)
|
||||
let obsoleteHashes: [String] = [String](
|
||||
pointer: cPushData.pointee.obsolete,
|
||||
count: cPushData.pointee.obsolete_len,
|
||||
defaultValue: []
|
||||
)
|
||||
let seqNo: Int64 = cPushData.pointee.seqno
|
||||
cPushData.deallocate()
|
||||
|
||||
return OutgoingConfResult(
|
||||
message: SharedConfigMessage(
|
||||
kind: variant.configMessageKind,
|
||||
seqNo: seqNo,
|
||||
data: pushData
|
||||
seqNo: result.seqNo,
|
||||
data: result.data
|
||||
),
|
||||
namespace: variant.namespace,
|
||||
obsoleteHashes: obsoleteHashes
|
||||
obsoleteHashes: result.obsoleteHashes
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -306,22 +354,22 @@ public enum SessionUtil {
|
|||
public static func markingAsPushed(
|
||||
message: SharedConfigMessage,
|
||||
serverHash: String,
|
||||
publicKey: String
|
||||
publicKey: String,
|
||||
using dependencies: Dependencies
|
||||
) -> ConfigDump? {
|
||||
return SessionUtil
|
||||
return dependencies.caches[.sessionUtil]
|
||||
.config(for: message.kind.configDumpVariant, publicKey: publicKey)
|
||||
.mutate { conf in
|
||||
guard conf != nil else { return nil }
|
||||
.mutate { config -> ConfigDump? in
|
||||
guard config != nil else { return nil }
|
||||
|
||||
// Mark the config as pushed
|
||||
var cHash: [CChar] = serverHash.cArray.nullTerminated()
|
||||
config_confirm_pushed(conf, message.seqNo, &cHash)
|
||||
config?.confirmPushed(seqNo: message.seqNo, hash: serverHash)
|
||||
|
||||
// Update the result to indicate whether the config needs to be dumped
|
||||
guard config_needs_dump(conf) else { return nil }
|
||||
guard config.needsPush else { return nil }
|
||||
|
||||
return try? SessionUtil.createDump(
|
||||
conf: conf,
|
||||
config: config,
|
||||
for: message.kind.configDumpVariant,
|
||||
publicKey: publicKey,
|
||||
timestampMs: (message.sentTimestamp.map { Int64($0) } ?? 0)
|
||||
|
@ -329,8 +377,11 @@ public enum SessionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static func configHashes(for publicKey: String) -> [String] {
|
||||
return Storage.shared
|
||||
public static func configHashes(
|
||||
for publicKey: String,
|
||||
using dependencies: Dependencies
|
||||
) -> [String] {
|
||||
return dependencies.storage
|
||||
.read { db -> Set<ConfigDump.Variant> in
|
||||
guard Identity.userExists(db) else { return [] }
|
||||
|
||||
|
@ -343,21 +394,11 @@ public enum SessionUtil {
|
|||
.defaulting(to: [])
|
||||
.map { variant -> [String] in
|
||||
/// Extract all existing hashes for any dumps associated with the given `publicKey`
|
||||
guard
|
||||
let conf = SessionUtil
|
||||
.config(for: variant, publicKey: publicKey)
|
||||
.wrappedValue,
|
||||
let hashList: UnsafeMutablePointer<config_string_list> = config_current_hashes(conf)
|
||||
else { return [] }
|
||||
|
||||
let result: [String] = [String](
|
||||
pointer: hashList.pointee.value,
|
||||
count: hashList.pointee.len,
|
||||
defaultValue: []
|
||||
)
|
||||
hashList.deallocate()
|
||||
|
||||
return result
|
||||
dependencies.caches[.sessionUtil]
|
||||
.config(for: variant, publicKey: publicKey)
|
||||
.wrappedValue
|
||||
.map { $0.currentHashes() }
|
||||
.defaulting(to: [])
|
||||
}
|
||||
.reduce([], +)
|
||||
}
|
||||
|
@ -367,7 +408,8 @@ public enum SessionUtil {
|
|||
public static func handleConfigMessages(
|
||||
_ db: Database,
|
||||
messages: [SharedConfigMessage],
|
||||
publicKey: String
|
||||
publicKey: String,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) throws {
|
||||
guard !messages.isEmpty else { return }
|
||||
guard !publicKey.isEmpty else { throw MessageReceiverError.noThread }
|
||||
|
@ -379,20 +421,11 @@ public enum SessionUtil {
|
|||
.sorted { lhs, rhs in lhs.key.processingOrder < rhs.key.processingOrder }
|
||||
.reduce(false) { prevNeedsPush, next -> Bool in
|
||||
let latestConfigSentTimestampMs: Int64 = Int64(next.value.compactMap { $0.sentTimestamp }.max() ?? 0)
|
||||
let needsPush: Bool = try SessionUtil
|
||||
let needsPush: Bool = try dependencies.caches[.sessionUtil]
|
||||
.config(for: next.key, publicKey: publicKey)
|
||||
.mutate { conf in
|
||||
.mutate { config in
|
||||
// Merge the messages
|
||||
var mergeHashes: [UnsafePointer<CChar>?] = next.value
|
||||
.map { message in (message.serverHash ?? "").cArray.nullTerminated() }
|
||||
.unsafeCopy()
|
||||
var mergeData: [UnsafePointer<UInt8>?] = next.value
|
||||
.map { message -> [UInt8] in message.data.bytes }
|
||||
.unsafeCopy()
|
||||
var mergeSize: [Int] = next.value.map { $0.data.count }
|
||||
config_merge(conf, &mergeHashes, &mergeData, &mergeSize, next.value.count)
|
||||
mergeHashes.forEach { $0?.deallocate() }
|
||||
mergeData.forEach { $0?.deallocate() }
|
||||
config?.merge(next.value)
|
||||
|
||||
// Apply the updated states to the database
|
||||
do {
|
||||
|
@ -400,56 +433,56 @@ public enum SessionUtil {
|
|||
case .userProfile:
|
||||
try SessionUtil.handleUserProfileUpdate(
|
||||
db,
|
||||
in: conf,
|
||||
mergeNeedsDump: config_needs_dump(conf),
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs
|
||||
in: config,
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .contacts:
|
||||
try SessionUtil.handleContactsUpdate(
|
||||
db,
|
||||
in: conf,
|
||||
mergeNeedsDump: config_needs_dump(conf),
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs
|
||||
in: config,
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .convoInfoVolatile:
|
||||
try SessionUtil.handleConvoInfoVolatileUpdate(
|
||||
db,
|
||||
in: conf,
|
||||
mergeNeedsDump: config_needs_dump(conf)
|
||||
in: config,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .userGroups:
|
||||
try SessionUtil.handleGroupsUpdate(
|
||||
try SessionUtil.handleUserGroupsUpdate(
|
||||
db,
|
||||
in: conf,
|
||||
mergeNeedsDump: config_needs_dump(conf),
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs
|
||||
in: config,
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .groupInfo:
|
||||
try SessionUtil.handleGroupInfoUpdate(
|
||||
db,
|
||||
in: conf,
|
||||
mergeNeedsDump: config_needs_dump(conf),
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs
|
||||
in: config,
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .groupMembers:
|
||||
try SessionUtil.handleGroupMembersUpdate(
|
||||
db,
|
||||
in: conf,
|
||||
mergeNeedsDump: config_needs_dump(conf),
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs
|
||||
in: config,
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
case .groupKeys:
|
||||
try SessionUtil.handleGroupKeysUpdate(
|
||||
db,
|
||||
in: conf,
|
||||
mergeNeedsDump: config_needs_dump(conf),
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs
|
||||
in: config,
|
||||
latestConfigSentTimestampMs: latestConfigSentTimestampMs,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -460,7 +493,7 @@ public enum SessionUtil {
|
|||
|
||||
// Need to check if the config needs to be dumped (this might have changed
|
||||
// after handling the merge changes)
|
||||
guard config_needs_dump(conf) else {
|
||||
guard config.needsDump else {
|
||||
try ConfigDump
|
||||
.filter(
|
||||
ConfigDump.Columns.variant == next.key &&
|
||||
|
@ -471,17 +504,17 @@ public enum SessionUtil {
|
|||
ConfigDump.Columns.timestampMs.set(to: latestConfigSentTimestampMs)
|
||||
)
|
||||
|
||||
return config_needs_push(conf)
|
||||
return config.needsPush
|
||||
}
|
||||
|
||||
try SessionUtil.createDump(
|
||||
conf: conf,
|
||||
config: config,
|
||||
for: next.key,
|
||||
publicKey: publicKey,
|
||||
timestampMs: latestConfigSentTimestampMs
|
||||
)?.save(db)
|
||||
|
||||
return config_needs_push(conf)
|
||||
return config.needsPush
|
||||
}
|
||||
|
||||
// Update the 'needsPush' state as needed
|
||||
|
@ -498,15 +531,6 @@ public enum SessionUtil {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Internal Convenience
|
||||
|
||||
fileprivate extension SessionUtil {
|
||||
struct ConfigKey: Hashable {
|
||||
let variant: ConfigDump.Variant
|
||||
let publicKey: String
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Convenience
|
||||
|
||||
public extension SessionUtil {
|
||||
|
@ -543,3 +567,83 @@ public extension SessionUtil {
|
|||
return String(cString: cFullUrl)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Convenience
|
||||
|
||||
private extension Int32 {
|
||||
func returning(_ config: SessionUtil.Config?, orThrow description: String, error: [CChar]) throws -> SessionUtil.Config {
|
||||
guard self == 0, let config: SessionUtil.Config = config else {
|
||||
SNLog("[SessionUtil Error] \(description): \(String(cString: error))")
|
||||
throw SessionUtilError.unableToCreateConfigObject
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SessionUtil Cache
|
||||
|
||||
public extension SessionUtil {
|
||||
class Cache: SessionUtilCacheType {
|
||||
public struct Key: Hashable {
|
||||
let variant: ConfigDump.Variant
|
||||
let publicKey: String
|
||||
}
|
||||
|
||||
private var configStore: [SessionUtil.Cache.Key: Atomic<SessionUtil.Config?>] = [:]
|
||||
|
||||
public var isEmpty: Bool { configStore.isEmpty }
|
||||
|
||||
/// Returns `true` if there is a config which needs to be pushed, but returns `false` if the configs are all up to date or haven't been
|
||||
/// loaded yet (eg. fresh install)
|
||||
public var needsSync: Bool { configStore.contains { _, atomicConf in atomicConf.needsPush } }
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
public func setConfig(for variant: ConfigDump.Variant, publicKey: String, to config: SessionUtil.Config?) {
|
||||
configStore[Key(variant: variant, publicKey: publicKey)] = Atomic(config)
|
||||
}
|
||||
|
||||
public func config(
|
||||
for variant: ConfigDump.Variant,
|
||||
publicKey: String
|
||||
) -> Atomic<Config?> {
|
||||
return (
|
||||
configStore[Key(variant: variant, publicKey: publicKey)] ??
|
||||
Atomic(nil)
|
||||
)
|
||||
}
|
||||
|
||||
public func removeAll() {
|
||||
configStore.removeAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Cache {
|
||||
static let sessionUtil: CacheInfo.Config<SessionUtilCacheType, SessionUtilImmutableCacheType> = CacheInfo.create(
|
||||
createInstance: { SessionUtil.Cache() },
|
||||
mutableInstance: { $0 },
|
||||
immutableInstance: { $0 }
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - SessionUtilCacheType
|
||||
|
||||
/// This is a read-only version of the `SessionUtil.Cache` designed to avoid unintentionally mutating the instance in a
|
||||
/// non-thread-safe way
|
||||
public protocol SessionUtilImmutableCacheType: ImmutableCacheType {
|
||||
var isEmpty: Bool { get }
|
||||
var needsSync: Bool { get }
|
||||
|
||||
func config(for variant: ConfigDump.Variant, publicKey: String) -> Atomic<SessionUtil.Config?>
|
||||
}
|
||||
|
||||
public protocol SessionUtilCacheType: SessionUtilImmutableCacheType, MutableCacheType {
|
||||
var isEmpty: Bool { get }
|
||||
var needsSync: Bool { get }
|
||||
|
||||
func setConfig(for variant: ConfigDump.Variant, publicKey: String, to config: SessionUtil.Config?)
|
||||
func config(for variant: ConfigDump.Variant, publicKey: String) -> Atomic<SessionUtil.Config?>
|
||||
func removeAll()
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import Foundation
|
|||
|
||||
public enum SessionUtilError: Error {
|
||||
case unableToCreateConfigObject
|
||||
case nilConfigObject
|
||||
case invalidConfigObject
|
||||
case userDoesNotExist
|
||||
case getOrConstructFailedUnexpectedly
|
||||
case processingLoopLimitReached
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtil
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public extension SessionUtil {
|
||||
enum Config {
|
||||
case object(UnsafeMutablePointer<config_object>)
|
||||
case groupKeys(
|
||||
UnsafeMutablePointer<config_group_keys>,
|
||||
info: UnsafeMutablePointer<config_object>,
|
||||
members: UnsafeMutablePointer<config_object>
|
||||
)
|
||||
|
||||
// MARK: - Variables
|
||||
|
||||
var needsPush: Bool {
|
||||
switch self {
|
||||
case .object(let conf): return config_needs_push(conf)
|
||||
|
||||
case .groupKeys(let conf, _, _):
|
||||
var pushResult: UnsafePointer<UInt8>? = nil
|
||||
var pushResultLen: Int = 0
|
||||
|
||||
return groups_keys_pending_config(conf, &pushResult, &pushResultLen)
|
||||
}
|
||||
}
|
||||
|
||||
var needsDump: Bool {
|
||||
switch self {
|
||||
case .object(let conf): return config_needs_push(conf)
|
||||
case .groupKeys(let conf, _, _): return groups_keys_needs_dump(conf)
|
||||
}
|
||||
}
|
||||
|
||||
var lastError: String {
|
||||
switch self {
|
||||
case .object(let conf): return String(cString: conf.pointee.last_error)
|
||||
case .groupKeys(let conf, _, _): return String(cString: conf.pointee.last_error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
static func from(_ conf: UnsafeMutablePointer<config_object>?) -> Config? {
|
||||
return conf.map { .object($0) }
|
||||
}
|
||||
|
||||
static func from(
|
||||
_ conf: UnsafeMutablePointer<config_group_keys>?,
|
||||
info: UnsafeMutablePointer<config_object>,
|
||||
members: UnsafeMutablePointer<config_object>
|
||||
) -> Config? {
|
||||
return conf.map { .groupKeys($0, info: info, members: members) }
|
||||
}
|
||||
|
||||
func push() throws -> (data: Data, seqNo: Int64, obsoleteHashes: [String]) {
|
||||
switch self {
|
||||
case .object(let conf):
|
||||
var cPushData: UnsafeMutablePointer<config_push_data>!
|
||||
|
||||
try CExceptionHelper.performSafely {
|
||||
cPushData = config_push(conf)
|
||||
}
|
||||
|
||||
let pushData: Data = Data(
|
||||
bytes: cPushData.pointee.config,
|
||||
count: cPushData.pointee.config_len
|
||||
)
|
||||
let obsoleteHashes: [String] = [String](
|
||||
pointer: cPushData.pointee.obsolete,
|
||||
count: cPushData.pointee.obsolete_len,
|
||||
defaultValue: []
|
||||
)
|
||||
let seqNo: Int64 = cPushData.pointee.seqno
|
||||
cPushData.deallocate()
|
||||
|
||||
return (pushData, seqNo, obsoleteHashes)
|
||||
|
||||
case .groupKeys(let conf, _, _):
|
||||
var pushResult: UnsafePointer<UInt8>!
|
||||
var pushResultLen: Int = 0
|
||||
|
||||
guard groups_keys_pending_config(conf, &pushResult, &pushResultLen) else {
|
||||
return (Data(), 0, [])
|
||||
}
|
||||
|
||||
return (Data(bytes: pushResult, count: pushResultLen), 0, [])
|
||||
}
|
||||
}
|
||||
|
||||
func confirmPushed(
|
||||
seqNo: Int64,
|
||||
hash: String
|
||||
) {
|
||||
var cHash: [CChar] = hash.cArray.nullTerminated()
|
||||
|
||||
switch self {
|
||||
case .object(let conf): return config_confirm_pushed(conf, seqNo, &cHash)
|
||||
case .groupKeys: return // No need to do anything here
|
||||
}
|
||||
}
|
||||
|
||||
func dump() throws -> Data? {
|
||||
var dumpResult: UnsafeMutablePointer<UInt8>? = nil
|
||||
var dumpResultLen: Int = 0
|
||||
|
||||
switch self {
|
||||
case .object(let conf):
|
||||
try CExceptionHelper.performSafely {
|
||||
config_dump(conf, &dumpResult, &dumpResultLen)
|
||||
}
|
||||
|
||||
case .groupKeys(let conf, _, _):
|
||||
try CExceptionHelper.performSafely {
|
||||
groups_keys_dump(conf, &dumpResult, &dumpResultLen)
|
||||
}
|
||||
}
|
||||
|
||||
guard let dumpResult: UnsafeMutablePointer<UInt8> = dumpResult else { return nil }
|
||||
|
||||
let dumpData: Data = Data(bytes: dumpResult, count: dumpResultLen)
|
||||
dumpResult.deallocate()
|
||||
|
||||
return dumpData
|
||||
}
|
||||
|
||||
func currentHashes() -> [String] {
|
||||
switch self {
|
||||
case .object(let conf):
|
||||
guard let hashList: UnsafeMutablePointer<config_string_list> = config_current_hashes(conf) else {
|
||||
return []
|
||||
}
|
||||
|
||||
let result: [String] = [String](
|
||||
pointer: hashList.pointee.value,
|
||||
count: hashList.pointee.len,
|
||||
defaultValue: []
|
||||
)
|
||||
hashList.deallocate()
|
||||
|
||||
return result
|
||||
|
||||
case .groupKeys(var conf): return []
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult func merge(_ messages: [SharedConfigMessage]) -> Int {
|
||||
switch self {
|
||||
case .object(let conf):
|
||||
var mergeHashes: [UnsafePointer<CChar>?] = messages
|
||||
.map { message in (message.serverHash ?? "").cArray.nullTerminated() }
|
||||
.unsafeCopy()
|
||||
var mergeData: [UnsafePointer<UInt8>?] = messages
|
||||
.map { message -> [UInt8] in message.data.bytes }
|
||||
.unsafeCopy()
|
||||
var mergeSize: [Int] = messages.map { $0.data.count }
|
||||
let numMerged: Int32 = config_merge(
|
||||
conf,
|
||||
&mergeHashes,
|
||||
&mergeData,
|
||||
&mergeSize,
|
||||
messages.count
|
||||
)
|
||||
mergeHashes.forEach { $0?.deallocate() }
|
||||
mergeData.forEach { $0?.deallocate() }
|
||||
|
||||
return Int(numMerged)
|
||||
|
||||
case .groupKeys(let conf, let infoConf, let membersConf):
|
||||
return messages
|
||||
.map { message -> Bool in
|
||||
var data: [UInt8] = Array(message.data)
|
||||
|
||||
return groups_keys_load_message(
|
||||
conf,
|
||||
&data,
|
||||
data.count,
|
||||
Int64(message.sentTimestamp ?? 0),
|
||||
infoConf,
|
||||
membersConf
|
||||
)
|
||||
}
|
||||
.filter { $0 }
|
||||
.count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Optional Convenience
|
||||
|
||||
public extension Optional where Wrapped == SessionUtil.Config {
|
||||
// MARK: - Variables
|
||||
|
||||
var needsPush: Bool {
|
||||
switch self {
|
||||
case .some(let config): return config.needsPush
|
||||
case .none: return false
|
||||
}
|
||||
}
|
||||
|
||||
var needsDump: Bool {
|
||||
switch self {
|
||||
case .some(let config): return config.needsDump
|
||||
case .none: return false
|
||||
}
|
||||
}
|
||||
|
||||
var lastError: String {
|
||||
switch self {
|
||||
case .some(let config): return config.lastError
|
||||
case .none: return "Nil Config"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Functions
|
||||
|
||||
func confirmPushed(seqNo: Int64, hash: String) {
|
||||
switch self {
|
||||
case .some(let config): return config.confirmPushed(seqNo: seqNo, hash: hash)
|
||||
case .none: return
|
||||
}
|
||||
}
|
||||
|
||||
func dump() throws -> Data? {
|
||||
switch self {
|
||||
case .some(let config): return try config.dump()
|
||||
case .none: return nil
|
||||
}
|
||||
}
|
||||
|
||||
func currentHashes() -> [String] {
|
||||
switch self {
|
||||
case .some(let config): return config.currentHashes()
|
||||
case .none: return []
|
||||
}
|
||||
}
|
||||
|
||||
func merge(_ messages: [SharedConfigMessage]) {
|
||||
switch self {
|
||||
case .some(let config): config.merge(messages)
|
||||
case .none: return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Atomic Convenience
|
||||
|
||||
public extension Atomic where Value == Optional<SessionUtil.Config> {
|
||||
var needsPush: Bool { return wrappedValue.needsPush }
|
||||
var needsDump: Bool { return wrappedValue.needsDump }
|
||||
}
|
|
@ -268,21 +268,22 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
|
|||
}
|
||||
|
||||
/// This method marks a thread as read and depending on the target may also update the interactions within a thread as read
|
||||
public func markAsRead(target: ReadTarget) {
|
||||
public func markAsRead(target: ReadTarget, using dependencies: Dependencies = Dependencies()) {
|
||||
// Store the logic to mark a thread as read (to paths need to run this)
|
||||
let threadId: String = self.threadId
|
||||
let threadWasMarkedUnread: Bool? = self.threadWasMarkedUnread
|
||||
let markThreadAsReadIfNeeded: () -> () = {
|
||||
let markThreadAsReadIfNeeded: (Dependencies) -> () = { dependencies in
|
||||
// Only make this change if needed (want to avoid triggering a thread update
|
||||
// if not needed)
|
||||
guard threadWasMarkedUnread == true else { return }
|
||||
|
||||
Storage.shared.writeAsync { db in
|
||||
dependencies.storage.writeAsync { db in
|
||||
try SessionThread
|
||||
.filter(id: threadId)
|
||||
.updateAllAndConfig(
|
||||
db,
|
||||
SessionThread.Columns.markedAsUnread.set(to: false)
|
||||
SessionThread.Columns.markedAsUnread.set(to: false),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -290,7 +291,7 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
|
|||
// Determine what we want to mark as read
|
||||
switch target {
|
||||
// Only mark the thread as read
|
||||
case .thread: markThreadAsReadIfNeeded()
|
||||
case .thread: markThreadAsReadIfNeeded(dependencies)
|
||||
|
||||
// We want to mark both the thread and interactions as read
|
||||
case .threadAndInteractions(let interactionId):
|
||||
|
@ -299,7 +300,7 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
|
|||
let targetInteractionId: Int64 = (interactionId ?? self.interactionId)
|
||||
else {
|
||||
// No unread interactions so just mark the thread as read if needed
|
||||
markThreadAsReadIfNeeded()
|
||||
markThreadAsReadIfNeeded(dependencies)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -308,8 +309,8 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
|
|||
let threadIsBlocked: Bool? = self.threadIsBlocked
|
||||
let threadIsMessageRequest: Bool? = self.threadIsMessageRequest
|
||||
|
||||
Storage.shared.writeAsync { db in
|
||||
markThreadAsReadIfNeeded()
|
||||
dependencies.storage.writeAsync { db in
|
||||
markThreadAsReadIfNeeded(dependencies)
|
||||
|
||||
try Interaction.markAsRead(
|
||||
db,
|
||||
|
@ -323,24 +324,26 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
|
|||
threadVariant: threadVariant,
|
||||
isBlocked: threadIsBlocked,
|
||||
isMessageRequest: threadIsMessageRequest
|
||||
)
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This method will mark a thread as read
|
||||
public func markAsUnread() {
|
||||
public func markAsUnread(using dependencies: Dependencies = Dependencies()) {
|
||||
guard self.threadWasMarkedUnread != true else { return }
|
||||
|
||||
let threadId: String = self.threadId
|
||||
|
||||
Storage.shared.writeAsync { db in
|
||||
dependencies.storage.writeAsync { db in
|
||||
try SessionThread
|
||||
.filter(id: threadId)
|
||||
.updateAllAndConfig(
|
||||
db,
|
||||
SessionThread.Columns.markedAsUnread.set(to: true)
|
||||
SessionThread.Columns.markedAsUnread.set(to: true),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,11 @@ public extension Identity {
|
|||
/// One case which can happen is if the app crashed during onboarding the user can be left in an invalid
|
||||
/// state (ie. with no display name) - the user would be asked to enter one on a subsequent launch to
|
||||
/// resolve the invalid state
|
||||
static func userCompletedRequiredOnboarding(_ db: Database? = nil) -> Bool {
|
||||
Identity.userExists(db) &&
|
||||
!Profile.fetchOrCreateCurrentUser(db).name.isEmpty
|
||||
static func userCompletedRequiredOnboarding(
|
||||
_ db: Database? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Bool {
|
||||
Identity.userExists(db, using: dependencies) &&
|
||||
!Profile.fetchOrCreateCurrentUser(db, using: dependencies).name.isEmpty
|
||||
}
|
||||
}
|
||||
|
|
|
@ -378,7 +378,7 @@ class MessageSendJobSpec: QuickSpec {
|
|||
)
|
||||
|
||||
expect(mockJobRunner)
|
||||
.to(call(.exactly(times: 1), matchingParameters: true) {
|
||||
.to(call(.exactly(times: 1), matchingParameters: .all) {
|
||||
$0.insert(
|
||||
any(),
|
||||
job: Job(
|
||||
|
|
|
@ -17,6 +17,7 @@ extension LibSessionSpec {
|
|||
context("GROUP_INFO") {
|
||||
// MARK: - generates config correctly
|
||||
it("generates config correctly") {
|
||||
let userSeed: Data = Data(hex: "0123456789abcdef0123456789abcdef")
|
||||
let seed: Data = Data(
|
||||
hex: "0123456789abcdef0123456789abcdeffedcba9876543210fedcba9876543210"
|
||||
)
|
||||
|
|
|
@ -1,24 +1,103 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import Sodium
|
||||
import SessionUtil
|
||||
import SessionUtilitiesKit
|
||||
import SessionMessagingKit
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class SessionUtilSpec: QuickSpec {
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
var mockStorage: Storage!
|
||||
var mockCrypto: MockCrypto!
|
||||
var mockCaches: MockCaches!
|
||||
var mockGeneralCache: MockGeneralCache!
|
||||
var mockSessionUtilCache: MockSessionUtilCache!
|
||||
var dependencies: Dependencies!
|
||||
|
||||
var createGroupOutput: (identityKeyPair: KeyPair, group: ClosedGroup, members: [GroupMember])!
|
||||
var userGroupsConfig: SessionUtil.Config!
|
||||
|
||||
describe("SessionUtil") {
|
||||
// MARK: - Parsing URLs
|
||||
// MARK: - Configuration
|
||||
|
||||
beforeEach {
|
||||
mockStorage = SynchronousStorage(
|
||||
customWriter: try! DatabaseQueue(),
|
||||
customMigrationTargets: [
|
||||
SNUtilitiesKit.self,
|
||||
SNMessagingKit.self
|
||||
]
|
||||
)
|
||||
mockCrypto = MockCrypto()
|
||||
mockCaches = MockCaches()
|
||||
mockGeneralCache = MockGeneralCache()
|
||||
mockSessionUtilCache = MockSessionUtilCache()
|
||||
dependencies = Dependencies(
|
||||
storage: mockStorage,
|
||||
crypto: mockCrypto,
|
||||
caches: mockCaches,
|
||||
dateNow: Date(timeIntervalSince1970: 1234567890),
|
||||
forceSynchronous: true
|
||||
)
|
||||
mockCaches[.general] = mockGeneralCache
|
||||
mockCaches[.sessionUtil] = mockSessionUtilCache
|
||||
|
||||
mockStorage.write { db in
|
||||
try Identity(variant: .x25519PublicKey, data: Data(hex: TestConstants.publicKey)).insert(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).insert(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.edPublicKey)).insert(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).insert(db)
|
||||
}
|
||||
|
||||
mockCrypto
|
||||
.when { [dependencies = dependencies!] crypto in
|
||||
crypto.generate(
|
||||
.ed25519KeyPair(
|
||||
seed: any(),
|
||||
using: dependencies
|
||||
)
|
||||
)
|
||||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(
|
||||
fromHex: "cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"
|
||||
)!.bytes,
|
||||
secretKey: Data.data(
|
||||
fromHex: "0123456789abcdef0123456789abcdeffedcba9876543210fedcba9876543210" +
|
||||
"cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"
|
||||
)!.bytes
|
||||
)
|
||||
)
|
||||
|
||||
mockGeneralCache.when { $0.encodedPublicKey }.thenReturn("05\(TestConstants.publicKey)")
|
||||
mockSessionUtilCache
|
||||
.when { $0.setConfig(for: any(), publicKey: any(), to: any()) }
|
||||
.thenReturn(())
|
||||
}
|
||||
|
||||
afterEach {
|
||||
mockStorage = nil
|
||||
mockCrypto = nil
|
||||
mockCaches = nil
|
||||
mockGeneralCache = nil
|
||||
dependencies = nil
|
||||
|
||||
createGroupOutput = nil
|
||||
userGroupsConfig = nil
|
||||
}
|
||||
|
||||
// MARK: - when parsing a community url
|
||||
context("when parsing a community url") {
|
||||
// MARK: -- handles the example urls correctly
|
||||
it("handles the example urls correctly") {
|
||||
let validUrls: [String] = [
|
||||
[
|
||||
|
@ -82,6 +161,7 @@ class SessionUtilSpec: QuickSpec {
|
|||
expect(processedPublicKeys).to(equal(expectedPublicKeys))
|
||||
}
|
||||
|
||||
// MARK: -- handles the r prefix if present
|
||||
it("handles the r prefix if present") {
|
||||
let info = SessionUtil.parseCommunity(
|
||||
url: [
|
||||
|
@ -95,6 +175,7 @@ class SessionUtilSpec: QuickSpec {
|
|||
expect(info?.publicKey).to(equal("658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231c"))
|
||||
}
|
||||
|
||||
// MARK: -- fails if no scheme is provided
|
||||
it("fails if no scheme is provided") {
|
||||
let info = SessionUtil.parseCommunity(
|
||||
url: [
|
||||
|
@ -108,6 +189,7 @@ class SessionUtilSpec: QuickSpec {
|
|||
expect(info?.publicKey).to(beNil())
|
||||
}
|
||||
|
||||
// MARK: -- fails if there is no room
|
||||
it("fails if there is no room") {
|
||||
let info = SessionUtil.parseCommunity(
|
||||
url: [
|
||||
|
@ -121,6 +203,7 @@ class SessionUtilSpec: QuickSpec {
|
|||
expect(info?.publicKey).to(beNil())
|
||||
}
|
||||
|
||||
// MARK: -- fails if there is no public key parameter
|
||||
it("fails if there is no public key parameter") {
|
||||
let info = SessionUtil.parseCommunity(
|
||||
url: "https://sessionopengroup.co/r/main"
|
||||
|
@ -131,6 +214,7 @@ class SessionUtilSpec: QuickSpec {
|
|||
expect(info?.publicKey).to(beNil())
|
||||
}
|
||||
|
||||
// MARK: -- fails if the public key parameter is not 64 characters
|
||||
it("fails if the public key parameter is not 64 characters") {
|
||||
let info = SessionUtil.parseCommunity(
|
||||
url: [
|
||||
|
@ -144,6 +228,7 @@ class SessionUtilSpec: QuickSpec {
|
|||
expect(info?.publicKey).to(beNil())
|
||||
}
|
||||
|
||||
// MARK: -- fails if the public key parameter is not a hex string
|
||||
it("fails if the public key parameter is not a hex string") {
|
||||
let info = SessionUtil.parseCommunity(
|
||||
url: [
|
||||
|
@ -157,6 +242,7 @@ class SessionUtilSpec: QuickSpec {
|
|||
expect(info?.publicKey).to(beNil())
|
||||
}
|
||||
|
||||
// MARK: -- maintains the same TLS
|
||||
it("maintains the same TLS") {
|
||||
let server1 = SessionUtil.parseCommunity(
|
||||
url: [
|
||||
|
@ -175,6 +261,7 @@ class SessionUtilSpec: QuickSpec {
|
|||
expect(server2).to(equal("https://sessionopengroup.co"))
|
||||
}
|
||||
|
||||
// MARK: -- maintains the same port
|
||||
it("maintains the same port") {
|
||||
let server1 = SessionUtil.parseCommunity(
|
||||
url: [
|
||||
|
@ -194,19 +281,380 @@ class SessionUtilSpec: QuickSpec {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Generating URLs
|
||||
|
||||
// MARK: - when generating a url
|
||||
context("when generating a url") {
|
||||
// MARK: -- generates the url correctly
|
||||
it("generates the url correctly") {
|
||||
expect(SessionUtil.communityUrlFor(server: "server", roomToken: "room", publicKey: "f8fec9b701000000ffffffff0400008000000000000000000000000000000000"))
|
||||
.to(equal("server/room?public_key=f8fec9b701000000ffffffff0400008000000000000000000000000000000000"))
|
||||
}
|
||||
|
||||
// MARK: -- maintains the casing provided
|
||||
it("maintains the casing provided") {
|
||||
expect(SessionUtil.communityUrlFor(server: "SeRVer", roomToken: "RoOM", publicKey: "f8fec9b701000000ffffffff0400008000000000000000000000000000000000"))
|
||||
.to(equal("SeRVer/RoOM?public_key=f8fec9b701000000ffffffff0400008000000000000000000000000000000000"))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - when creating a group
|
||||
context("when creating a group") {
|
||||
beforeEach {
|
||||
var userGroupsConf: UnsafeMutablePointer<config_object>!
|
||||
var secretKey: [UInt8] = Array(Data(hex: TestConstants.edSecretKey))
|
||||
_ = user_groups_init(&userGroupsConf, &secretKey, nil, 0, nil)
|
||||
userGroupsConfig = .object(userGroupsConf)
|
||||
|
||||
mockSessionUtilCache
|
||||
.when { $0.config(for: .userGroups, publicKey: any()) }
|
||||
.thenReturn(Atomic(userGroupsConfig))
|
||||
}
|
||||
|
||||
// MARK: -- throws when there is no user ed25519 keyPair
|
||||
it("throws when there is no user ed25519 keyPair") {
|
||||
var resultError: Error? = nil
|
||||
|
||||
mockStorage.write { db in
|
||||
try Identity.filter(id: .ed25519PublicKey).deleteAll(db)
|
||||
try Identity.filter(id: .ed25519SecretKey).deleteAll(db)
|
||||
|
||||
do {
|
||||
_ = try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
catch { resultError = error }
|
||||
}
|
||||
|
||||
expect(resultError).to(matchError(MessageSenderError.noKeyPair))
|
||||
}
|
||||
|
||||
// MARK: -- throws when it fails to generate a new identity ed25519 keyPair
|
||||
it("throws when it fails to generate a new identity ed25519 keyPair") {
|
||||
var resultError: Error? = nil
|
||||
|
||||
mockCrypto
|
||||
.when { [dependencies = dependencies!] crypto in
|
||||
crypto.generate(
|
||||
.ed25519KeyPair(
|
||||
seed: any(),
|
||||
using: dependencies
|
||||
)
|
||||
)
|
||||
}
|
||||
.thenReturn(nil)
|
||||
|
||||
mockStorage.write { db in
|
||||
do {
|
||||
_ = try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
catch { resultError = error }
|
||||
}
|
||||
|
||||
expect(resultError).to(matchError(MessageSenderError.noKeyPair))
|
||||
}
|
||||
|
||||
// MARK: -- throws when given an invalid member id
|
||||
it("throws when given an invalid member id") {
|
||||
var resultError: Error? = nil
|
||||
|
||||
mockStorage.write { db in
|
||||
do {
|
||||
_ = try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [(
|
||||
id: "123456",
|
||||
profile: Profile(
|
||||
id: "123456",
|
||||
name: "",
|
||||
lastNameUpdate: 0,
|
||||
lastProfilePictureUpdate: 0,
|
||||
lastBlocksCommunityMessageRequests: 0
|
||||
)
|
||||
)],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
catch { resultError = error }
|
||||
}
|
||||
|
||||
expect(resultError).to(matchError(
|
||||
NSError(
|
||||
domain: "cpp_exception",
|
||||
code: -2,
|
||||
userInfo: [
|
||||
NSLocalizedDescriptionKey: "Invalid session ID: expected 66 hex digits starting with 05; got 123456"
|
||||
]
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
// MARK: -- returns the correct identity keyPair
|
||||
it("returns the correct identity keyPair") {
|
||||
createGroupOutput = mockStorage.write(using: dependencies) { db in
|
||||
try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
expect(createGroupOutput.identityKeyPair.publicKey.toHexString())
|
||||
.to(equal("cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"))
|
||||
expect(createGroupOutput.identityKeyPair.secretKey.toHexString())
|
||||
.to(equal(
|
||||
"0123456789abcdef0123456789abcdeffedcba9876543210fedcba9876543210" +
|
||||
"cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"
|
||||
))
|
||||
}
|
||||
|
||||
// MARK: -- returns a closed group with the correct data set
|
||||
it("returns a closed group with the correct data set") {
|
||||
createGroupOutput = mockStorage.write(using: dependencies) { db in
|
||||
try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: "TestUrl",
|
||||
displayPictureFilename: "TestFilename",
|
||||
displayPictureEncryptionKey: Data([1, 2, 3]),
|
||||
members: [],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
expect(createGroupOutput.group.threadId)
|
||||
.to(equal("03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"))
|
||||
expect(createGroupOutput.group.groupIdentityPrivateKey?.toHexString())
|
||||
.to(equal(
|
||||
"0123456789abcdef0123456789abcdeffedcba9876543210fedcba9876543210" +
|
||||
"cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"
|
||||
))
|
||||
expect(createGroupOutput.group.name).to(equal("Testname"))
|
||||
expect(createGroupOutput.group.displayPictureUrl).to(equal("TestUrl"))
|
||||
expect(createGroupOutput.group.displayPictureFilename).to(equal("TestFilename"))
|
||||
expect(createGroupOutput.group.displayPictureEncryptionKey).to(equal(Data([1, 2, 3])))
|
||||
expect(createGroupOutput.group.formationTimestamp).to(equal(1234567890))
|
||||
expect(createGroupOutput.group.approved).to(beTrue())
|
||||
}
|
||||
|
||||
// MARK: -- returns the members setup correctly
|
||||
it("returns the members setup correctly") {
|
||||
createGroupOutput = mockStorage.write(using: dependencies) { db in
|
||||
try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [(
|
||||
id: "051111111111111111111111111111111111111111111111111111111111111111",
|
||||
profile: Profile(
|
||||
id: "051111111111111111111111111111111111111111111111111111111111111111",
|
||||
name: "TestName",
|
||||
lastNameUpdate: 0,
|
||||
profilePictureUrl: "testUrl",
|
||||
profileEncryptionKey: Data([1, 2, 3]),
|
||||
lastProfilePictureUpdate: 0,
|
||||
lastBlocksCommunityMessageRequests: 0
|
||||
)
|
||||
)],
|
||||
admins: [(
|
||||
id: "05\(TestConstants.publicKey)",
|
||||
profile: Profile(
|
||||
id: "05\(TestConstants.publicKey)",
|
||||
name: "TestName2",
|
||||
lastNameUpdate: 0,
|
||||
lastProfilePictureUpdate: 0,
|
||||
lastBlocksCommunityMessageRequests: 0
|
||||
)
|
||||
)],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
expect(createGroupOutput.members.count).to(equal(2))
|
||||
expect(createGroupOutput.members.map { $0.groupId })
|
||||
.to(equal([
|
||||
"03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
|
||||
"03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
|
||||
]))
|
||||
expect(createGroupOutput.members.map { $0.profileId }.asSet())
|
||||
.to(equal([
|
||||
"051111111111111111111111111111111111111111111111111111111111111111",
|
||||
"05\(TestConstants.publicKey)"
|
||||
]))
|
||||
expect(createGroupOutput.members.map { $0.role }.asSet())
|
||||
.to(equal([
|
||||
.standard,
|
||||
.admin
|
||||
]))
|
||||
expect(createGroupOutput.members.map { $0.isHidden }.asSet())
|
||||
.to(equal([
|
||||
false,
|
||||
false
|
||||
]))
|
||||
}
|
||||
|
||||
// MARK: -- adds the current user as an admin when not provided
|
||||
it("adds the current user as an admin when not provided") {
|
||||
createGroupOutput = mockStorage.write(using: dependencies) { db in
|
||||
try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [(
|
||||
id: "051111111111111111111111111111111111111111111111111111111111111111",
|
||||
profile: Profile(
|
||||
id: "051111111111111111111111111111111111111111111111111111111111111111",
|
||||
name: "TestName",
|
||||
lastNameUpdate: 0,
|
||||
lastProfilePictureUpdate: 0,
|
||||
lastBlocksCommunityMessageRequests: 0
|
||||
)
|
||||
)],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
expect(createGroupOutput.members.map { $0.groupId })
|
||||
.to(contain("03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"))
|
||||
expect(createGroupOutput.members.map { $0.profileId })
|
||||
.to(contain("05\(TestConstants.publicKey)"))
|
||||
expect(createGroupOutput.members.map { $0.role }).to(contain(.admin))
|
||||
}
|
||||
|
||||
// MARK: -- handles members without profile data correctly
|
||||
it("handles members without profile data correctly") {
|
||||
createGroupOutput = mockStorage.write(using: dependencies) { db in
|
||||
try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [(
|
||||
id: "051111111111111111111111111111111111111111111111111111111111111111",
|
||||
profile: nil
|
||||
)],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
expect(createGroupOutput.members.count).to(equal(2))
|
||||
expect(createGroupOutput.members.map { $0.groupId })
|
||||
.to(contain("03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"))
|
||||
expect(createGroupOutput.members.map { $0.profileId })
|
||||
.to(contain("051111111111111111111111111111111111111111111111111111111111111111"))
|
||||
expect(createGroupOutput.members.map { $0.role }).to(contain(.standard))
|
||||
}
|
||||
|
||||
// MARK: -- stores the config states in the cache correctly
|
||||
it("stores the config states in the cache correctly") {
|
||||
createGroupOutput = mockStorage.write(using: dependencies) { db in
|
||||
try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [(
|
||||
id: "051111111111111111111111111111111111111111111111111111111111111111",
|
||||
profile: nil
|
||||
)],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
expect(mockSessionUtilCache).to(call(.exactly(times: 3)) {
|
||||
$0.setConfig(for: any(), publicKey: any(), to: any())
|
||||
})
|
||||
expect(mockSessionUtilCache)
|
||||
.to(call(matchingParameters: .atLeast(2)) {
|
||||
$0.setConfig(
|
||||
for: .groupInfo,
|
||||
publicKey: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
|
||||
to: any()
|
||||
)
|
||||
})
|
||||
expect(mockSessionUtilCache)
|
||||
.to(call(matchingParameters: .atLeast(2)) {
|
||||
$0.setConfig(
|
||||
for: .groupMembers,
|
||||
publicKey: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
|
||||
to: any()
|
||||
)
|
||||
})
|
||||
expect(mockSessionUtilCache)
|
||||
.to(call(matchingParameters: .atLeast(2)) {
|
||||
$0.setConfig(
|
||||
for: .groupKeys,
|
||||
publicKey: "03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece",
|
||||
to: any()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: -- saves config dumps for the stored configs
|
||||
it("saves config dumps for the stored configs") {
|
||||
createGroupOutput = mockStorage.write(using: dependencies) { db in
|
||||
try SessionUtil.createGroup(
|
||||
db,
|
||||
name: "Testname",
|
||||
displayPictureUrl: nil,
|
||||
displayPictureFilename: nil,
|
||||
displayPictureEncryptionKey: nil,
|
||||
members: [(
|
||||
id: "051111111111111111111111111111111111111111111111111111111111111111",
|
||||
profile: nil
|
||||
)],
|
||||
admins: [],
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
let result: [ConfigDump]? = mockStorage.read(using: dependencies) { db in
|
||||
try ConfigDump.fetchAll(db)
|
||||
}
|
||||
|
||||
expect(result?.map { $0.variant }.asSet())
|
||||
.to(equal([.groupInfo, .groupKeys, .groupMembers]))
|
||||
expect(result?.map { $0.publicKey }.asSet())
|
||||
.to(equal(["03cbd569f56fb13ea95a3f0c05c331cc24139c0090feb412069dc49fab34406ece"]))
|
||||
expect(result?.map { $0.timestampMs }.asSet())
|
||||
.to(equal([1234567890000]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@ class SOGSMessageSpec: QuickSpec {
|
|||
_ = try? decoder.decode(OpenGroupAPI.Message.self, from: messageData)
|
||||
|
||||
expect(mockCrypto)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.verify(
|
||||
.signature(
|
||||
message: Data(base64Encoded: "VGVzdERhdGE=")!.bytes,
|
||||
|
@ -268,7 +268,7 @@ class SOGSMessageSpec: QuickSpec {
|
|||
_ = try? decoder.decode(OpenGroupAPI.Message.self, from: messageData)
|
||||
|
||||
expect(mockCrypto)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.verify(
|
||||
.signatureEd25519(
|
||||
Data(base64Encoded: "VGVzdFNpZ25hdHVyZQ==")!,
|
||||
|
|
|
@ -45,10 +45,10 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
)
|
||||
|
||||
mockStorage.write { db in
|
||||
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).insert(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)!).insert(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: TestConstants.edPublicKey)!).insert(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).insert(db)
|
||||
try Identity(variant: .x25519PublicKey, data: Data(hex: TestConstants.publicKey)).insert(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).insert(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.edPublicKey)).insert(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).insert(db)
|
||||
|
||||
try OpenGroup(
|
||||
server: "testServer",
|
||||
|
@ -76,8 +76,8 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockCrypto
|
||||
|
|
|
@ -146,10 +146,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
)
|
||||
|
||||
mockStorage.write { db in
|
||||
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).insert(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)!).insert(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: TestConstants.edPublicKey)!).insert(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).insert(db)
|
||||
try Identity(variant: .x25519PublicKey, data: Data(hex: TestConstants.publicKey)).insert(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).insert(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.edPublicKey)).insert(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).insert(db)
|
||||
|
||||
try testGroupThread.insert(db)
|
||||
try testOpenGroup.insert(db)
|
||||
|
@ -170,8 +170,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockCrypto
|
||||
|
@ -337,7 +337,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
openGroupManager.startPolling(using: dependencies)
|
||||
|
||||
expect(mockOGMCache)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.pollers = [
|
||||
"testserver": OpenGroupAPI.Poller(for: "testserver"),
|
||||
"testserver1": OpenGroupAPI.Poller(for: "testserver1")
|
||||
|
@ -350,7 +350,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
openGroupManager.startPolling(using: dependencies)
|
||||
|
||||
expect(mockOGMCache)
|
||||
.to(call(matchingParameters: true) { $0.isPolling = true })
|
||||
.to(call(matchingParameters: .all) { $0.isPolling = true })
|
||||
}
|
||||
|
||||
// MARK: -- does nothing if already polling
|
||||
|
@ -389,14 +389,14 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
it("removes all pollers") {
|
||||
openGroupManager.stopPolling(using: dependencies)
|
||||
|
||||
expect(mockOGMCache).to(call(matchingParameters: true) { $0.pollers = [:] })
|
||||
expect(mockOGMCache).to(call(matchingParameters: .all) { $0.pollers = [:] })
|
||||
}
|
||||
|
||||
// MARK: - updates the isPolling flag
|
||||
it("updates the isPolling flag") {
|
||||
openGroupManager.stopPolling(using: dependencies)
|
||||
|
||||
expect(mockOGMCache).to(call(matchingParameters: true) { $0.isPolling = false })
|
||||
expect(mockOGMCache).to(call(matchingParameters: .all) { $0.isPolling = false })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -853,7 +853,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
.sinkAndStore(in: &disposables)
|
||||
|
||||
expect(mockOGMCache)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.pollers = ["testserver": OpenGroupAPI.Poller(for: "testserver")]
|
||||
})
|
||||
}
|
||||
|
@ -1036,7 +1036,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
)
|
||||
}
|
||||
|
||||
expect(mockOGMCache).to(call(matchingParameters: true) { $0.pollers = [:] })
|
||||
expect(mockOGMCache).to(call(matchingParameters: .all) { $0.pollers = [:] })
|
||||
}
|
||||
|
||||
// MARK: ---- removes the open group
|
||||
|
@ -1615,7 +1615,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
|
||||
expect(mockOGMCache)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.pollers = ["testserver": OpenGroupAPI.Poller(for: "testserver")]
|
||||
})
|
||||
}
|
||||
|
@ -2698,8 +2698,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
mockStorage.write { db in
|
||||
let otherKey: String = TestConstants.publicKey.replacingOccurrences(of: "7", with: "6")
|
||||
|
||||
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)!).save(db)
|
||||
try Identity(variant: .x25519PublicKey, data: Data(hex: otherKey)).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).save(db)
|
||||
}
|
||||
|
||||
expect(
|
||||
|
@ -2724,8 +2724,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
isHidden: false
|
||||
).insert(db)
|
||||
|
||||
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: otherKey)).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).save(db)
|
||||
}
|
||||
|
||||
expect(
|
||||
|
@ -2753,8 +2753,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(fromHex: otherKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: otherKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockStorage.write { db in
|
||||
|
@ -2801,8 +2801,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
mockStorage.write { db in
|
||||
let otherKey: String = TestConstants.publicKey.replacingOccurrences(of: "7", with: "6")
|
||||
|
||||
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: otherKey)).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).save(db)
|
||||
}
|
||||
|
||||
expect(
|
||||
|
@ -2827,10 +2827,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
isHidden: false
|
||||
).insert(db)
|
||||
|
||||
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)!).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).save(db)
|
||||
try Identity(variant: .x25519PublicKey, data: Data(hex: otherKey)).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.publicKey)).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).save(db)
|
||||
}
|
||||
|
||||
expect(
|
||||
|
@ -2858,8 +2858,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(fromHex: otherKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: otherKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockStorage.write { db in
|
||||
|
@ -2870,10 +2870,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
isHidden: false
|
||||
).insert(db)
|
||||
|
||||
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)!).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).save(db)
|
||||
try Identity(variant: .x25519PublicKey, data: Data(hex: TestConstants.publicKey)).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.publicKey)).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).save(db)
|
||||
}
|
||||
|
||||
expect(
|
||||
|
@ -2945,8 +2945,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(fromHex: otherKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: otherKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -2976,8 +2976,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockStorage.write { db in
|
||||
|
@ -2988,10 +2988,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
isHidden: false
|
||||
).insert(db)
|
||||
|
||||
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)!).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).save(db)
|
||||
try Identity(variant: .x25519PublicKey, data: Data(hex: otherKey)).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: TestConstants.publicKey)).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).save(db)
|
||||
}
|
||||
|
||||
expect(
|
||||
|
@ -3018,8 +3018,8 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockStorage.write { db in
|
||||
|
@ -3032,10 +3032,10 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
isHidden: false
|
||||
).insert(db)
|
||||
|
||||
try Identity(variant: .x25519PublicKey, data: Data.data(fromHex: TestConstants.publicKey)!).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data.data(fromHex: TestConstants.privateKey)!).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data.data(fromHex: otherKey)!).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data.data(fromHex: TestConstants.edSecretKey)!).save(db)
|
||||
try Identity(variant: .x25519PublicKey, data: Data(hex: TestConstants.publicKey)).save(db)
|
||||
try Identity(variant: .x25519PrivateKey, data: Data(hex: TestConstants.privateKey)).save(db)
|
||||
try Identity(variant: .ed25519PublicKey, data: Data(hex: otherKey)).save(db)
|
||||
try Identity(variant: .ed25519SecretKey, data: Data(hex: TestConstants.edSecretKey)).save(db)
|
||||
}
|
||||
|
||||
expect(
|
||||
|
@ -3088,7 +3088,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
let publisher = OpenGroupManager.getDefaultRoomsIfNeeded(using: dependencies)
|
||||
|
||||
expect(mockOGMCache)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.defaultRoomsPublisher = publisher
|
||||
})
|
||||
}
|
||||
|
@ -3188,7 +3188,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
expect(error)
|
||||
.to(matchError(HTTPError.parsingFailed))
|
||||
expect(mockOGMCache)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.defaultRoomsPublisher = nil
|
||||
})
|
||||
}
|
||||
|
@ -3242,7 +3242,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
.sinkAndStore(in: &disposables)
|
||||
|
||||
expect(mockUserDefaults)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.set(
|
||||
testDate,
|
||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
||||
|
@ -3351,7 +3351,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
.sinkAndStore(in: &disposables)
|
||||
|
||||
expect(mockUserDefaults)
|
||||
.toNot(call(matchingParameters: true) {
|
||||
.toNot(call(matchingParameters: .all) {
|
||||
$0.set(
|
||||
dependencies.dateNow,
|
||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
||||
|
@ -3372,7 +3372,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
publisher.sinkAndStore(in: &disposables)
|
||||
|
||||
expect(mockOGMCache)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.groupImagePublishers = [OpenGroup.idFor(roomToken: "testRoom", server: "testServer"): publisher]
|
||||
})
|
||||
}
|
||||
|
@ -3433,7 +3433,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
.sinkAndStore(in: &disposables)
|
||||
|
||||
expect(mockUserDefaults)
|
||||
.to(call(matchingParameters: true) {
|
||||
.to(call(matchingParameters: .all) {
|
||||
$0.set(
|
||||
dependencies.dateNow,
|
||||
forKey: SNUserDefaults.Date.lastOpenGroupImageUpdate.rawValue
|
||||
|
|
|
@ -131,8 +131,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
"dt0eBaXneOBfr7qB8pHwwMZjtkOu1ED07T9nszgbWabBphUfWXe2U9K3PTRisSCI="
|
||||
)!,
|
||||
using: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.privateKey).bytes
|
||||
),
|
||||
using: Dependencies()
|
||||
)
|
||||
|
@ -159,8 +159,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
try MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: "TestMessage".data(using: .utf8)!,
|
||||
using: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.privateKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -185,8 +185,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
try MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: "TestMessage".data(using: .utf8)!,
|
||||
using: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.privateKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -203,8 +203,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
try MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: "TestMessage".data(using: .utf8)!,
|
||||
using: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.privateKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -219,8 +219,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
try MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: "TestMessage".data(using: .utf8)!,
|
||||
using: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.privateKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.privateKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -241,8 +241,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: Dependencies()
|
||||
)
|
||||
|
@ -263,8 +263,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -282,8 +282,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -315,8 +315,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -350,8 +350,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -371,8 +371,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -404,8 +404,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -437,8 +437,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -464,8 +464,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -489,8 +489,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -514,8 +514,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
@ -539,8 +539,8 @@ class MessageReceiverDecryptionSpec: QuickSpec {
|
|||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
|
|
@ -154,8 +154,8 @@ class MessageSenderEncryptionSpec: QuickSpec {
|
|||
}
|
||||
.thenReturn(
|
||||
KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.publicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
publicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockCrypto
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import SessionUtil
|
||||
import SessionMessagingKit
|
||||
|
||||
extension SessionUtil.Config: Mocked {
|
||||
static var mockValue: SessionUtil.Config = {
|
||||
var config: config_object = config_object()
|
||||
|
||||
return .object(&config)
|
||||
}()
|
||||
}
|
||||
|
||||
extension ConfigDump.Variant: Mocked {
|
||||
static var mockValue: ConfigDump.Variant = .userProfile
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import SessionUtilitiesKit
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class MockSessionUtilCache: Mock<SessionUtilCacheType>, SessionUtilCacheType {
|
||||
var isEmpty: Bool { return accept() as! Bool }
|
||||
var needsSync: Bool { return accept() as! Bool }
|
||||
|
||||
func setConfig(for variant: ConfigDump.Variant, publicKey: String, to config: SessionUtil.Config?) {
|
||||
accept(args: [variant, publicKey, config])
|
||||
}
|
||||
|
||||
func config(for variant: ConfigDump.Variant, publicKey: String) -> Atomic<SessionUtil.Config?> {
|
||||
return accept(args: [variant, publicKey]) as! Atomic<SessionUtil.Config?>
|
||||
}
|
||||
|
||||
func removeAll() {
|
||||
accept()
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ enum _001_InitialSetupMigration: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.create(table: Snode.self) { t in
|
||||
t.column(.address, .text).notNull()
|
||||
t.column(.port, .integer).notNull()
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _002_SetupStandardJobs: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try autoreleasepool {
|
||||
_ = try Job(
|
||||
variant: .getSnodePool,
|
||||
|
|
|
@ -10,7 +10,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
guard !SNUtilitiesKit.isRunningTests else { return Storage.update(progress: 1, for: self, in: target) }
|
||||
|
||||
SNLogNotTests("[Migration Error] Attempted to perform legacy migation")
|
||||
|
|
|
@ -14,7 +14,7 @@ enum _004_FlagMessageHashAsDeletedOrInvalid: Migration {
|
|||
/// messages from the beginning of time)
|
||||
static let minExpectedRunDuration: TimeInterval = 0.2
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.alter(table: SnodeReceivedMessageInfo.self) { t in
|
||||
t.add(.wasDeletedOrInvalid, .boolean)
|
||||
.indexed() // Faster querying
|
||||
|
|
|
@ -23,11 +23,11 @@ public enum GetSnodePoolJob: JobExecutor {
|
|||
// but we want to succeed this job immediately (since it's marked as blocking), this allows us
|
||||
// to block if we have no Snode pool and prevent other jobs from failing but avoids having to
|
||||
// wait if we already have a potentially valid snode pool
|
||||
guard !SnodeAPI.hasCachedSnodesIncludingExpired() else {
|
||||
guard !SnodeAPI.hasCachedSnodesIncludingExpired(using: dependencies) else {
|
||||
SNLog("[GetSnodePoolJob] Has valid cached pool, running async instead")
|
||||
SnodeAPI
|
||||
.getSnodePool()
|
||||
.subscribe(on: DispatchQueue.global(qos: .default))
|
||||
.getSnodePool(using: dependencies)
|
||||
.subscribe(on: DispatchQueue.global(qos: .default), using: dependencies)
|
||||
.sinkUntilComplete()
|
||||
return success(job, false, dependencies)
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public enum GetSnodePoolJob: JobExecutor {
|
|||
)
|
||||
}
|
||||
|
||||
public static func run(using dependencies: Dependencies = Dependencies()) {
|
||||
public static func run(using dependencies: Dependencies) {
|
||||
GetSnodePoolJob.run(
|
||||
Job(variant: .getSnodePool),
|
||||
queue: .global(qos: .background),
|
||||
|
|
|
@ -561,7 +561,12 @@ public enum OnionRequestAPI {
|
|||
if pathFailureCount >= pathFailureThreshold {
|
||||
dropGuardSnode(guardSnode)
|
||||
path.forEach { snode in
|
||||
SnodeAPI.handleError(withStatusCode: statusCode, data: data, forSnode: snode) // Intentionally don't throw
|
||||
SnodeAPI.handleError(
|
||||
withStatusCode: statusCode,
|
||||
data: data,
|
||||
forSnode: snode,
|
||||
using: dependencies
|
||||
) // Intentionally don't throw
|
||||
}
|
||||
|
||||
drop(path)
|
||||
|
@ -592,13 +597,15 @@ public enum OnionRequestAPI {
|
|||
snodeFailureCount += 1
|
||||
|
||||
if snodeFailureCount >= snodeFailureThreshold {
|
||||
SnodeAPI.handleError(withStatusCode: statusCode, data: data, forSnode: snode) // Intentionally don't throw
|
||||
do {
|
||||
try drop(snode)
|
||||
}
|
||||
catch {
|
||||
handleUnspecificError()
|
||||
}
|
||||
SnodeAPI.handleError(
|
||||
withStatusCode: statusCode,
|
||||
data: data,
|
||||
forSnode: snode,
|
||||
using: dependencies
|
||||
) // Intentionally don't throw
|
||||
|
||||
do { try drop(snode) }
|
||||
catch { handleUnspecificError() }
|
||||
}
|
||||
else {
|
||||
OnionRequestAPI.snodeFailureCount
|
||||
|
|
|
@ -61,21 +61,22 @@ public final class SnodeAPI {
|
|||
private static let snodeFailureThreshold: Int = 3
|
||||
private static let minSnodePoolCount: Int = 12
|
||||
|
||||
public static func currentOffsetTimestampMs() -> Int64 {
|
||||
return Int64(
|
||||
Int64(floor(Date().timeIntervalSince1970 * 1000)) +
|
||||
SnodeAPI.clockOffsetMs.wrappedValue
|
||||
)
|
||||
public static func currentOffsetTimestampMs(using dependencies: Dependencies = Dependencies()) -> Int64 {
|
||||
let clockOffsetMs: Int64 = SnodeAPI.clockOffsetMs.wrappedValue
|
||||
|
||||
return (Int64(floor(dependencies.dateNow.timeIntervalSince1970 * 1000)) + clockOffsetMs)
|
||||
}
|
||||
|
||||
// MARK: Snode Pool Interaction
|
||||
|
||||
private static var hasInsufficientSnodes: Bool { snodePool.wrappedValue.count < minSnodePoolCount }
|
||||
|
||||
private static func loadSnodePoolIfNeeded() {
|
||||
private static func loadSnodePoolIfNeeded(
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
guard !hasLoadedSnodePool.wrappedValue else { return }
|
||||
|
||||
let fetchedSnodePool: Set<Snode> = Storage.shared
|
||||
let fetchedSnodePool: Set<Snode> = dependencies.storage
|
||||
.read { db in try Snode.fetchSet(db) }
|
||||
.defaulting(to: [])
|
||||
|
||||
|
@ -83,9 +84,13 @@ public final class SnodeAPI {
|
|||
hasLoadedSnodePool.mutate { $0 = true }
|
||||
}
|
||||
|
||||
private static func setSnodePool(_ db: Database? = nil, to newValue: Set<Snode>) {
|
||||
private static func setSnodePool(
|
||||
_ db: Database? = nil,
|
||||
to newValue: Set<Snode>,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
guard let db: Database = db else {
|
||||
Storage.shared.write { db in setSnodePool(db, to: newValue) }
|
||||
dependencies.storage.write { db in setSnodePool(db, to: newValue, using: dependencies) }
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -111,10 +116,13 @@ public final class SnodeAPI {
|
|||
|
||||
// MARK: - Swarm Interaction
|
||||
|
||||
private static func loadSwarmIfNeeded(for publicKey: String) {
|
||||
private static func loadSwarmIfNeeded(
|
||||
for publicKey: String,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
guard !loadedSwarms.wrappedValue.contains(publicKey) else { return }
|
||||
|
||||
let updatedCacheForKey: Set<Snode> = Storage.shared
|
||||
let updatedCacheForKey: Set<Snode> = dependencies.storage
|
||||
.read { db in try Snode.fetchSet(db, publicKey: publicKey) }
|
||||
.defaulting(to: [])
|
||||
|
||||
|
@ -122,27 +130,38 @@ public final class SnodeAPI {
|
|||
loadedSwarms.mutate { $0.insert(publicKey) }
|
||||
}
|
||||
|
||||
private static func setSwarm(to newValue: Set<Snode>, for publicKey: String, persist: Bool = true) {
|
||||
private static func setSwarm(
|
||||
to newValue: Set<Snode>,
|
||||
for publicKey: String,
|
||||
persist: Bool = true,
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
swarmCache.mutate { $0[publicKey] = newValue }
|
||||
|
||||
guard persist else { return }
|
||||
|
||||
Storage.shared.write { db in
|
||||
dependencies.storage.write { db in
|
||||
try? newValue.save(db, key: publicKey)
|
||||
}
|
||||
}
|
||||
|
||||
public static func dropSnodeFromSwarmIfNeeded(_ snode: Snode, publicKey: String) {
|
||||
public static func dropSnodeFromSwarmIfNeeded(
|
||||
_ snode: Snode,
|
||||
publicKey: String,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
let swarmOrNil = swarmCache.wrappedValue[publicKey]
|
||||
guard var swarm = swarmOrNil, let index = swarm.firstIndex(of: snode) else { return }
|
||||
swarm.remove(at: index)
|
||||
setSwarm(to: swarm, for: publicKey)
|
||||
setSwarm(to: swarm, for: publicKey, using: dependencies)
|
||||
}
|
||||
|
||||
// MARK: - Public API
|
||||
|
||||
public static func hasCachedSnodesIncludingExpired() -> Bool {
|
||||
loadSnodePoolIfNeeded()
|
||||
public static func hasCachedSnodesIncludingExpired(
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Bool {
|
||||
loadSnodePoolIfNeeded(using: dependencies)
|
||||
|
||||
return !hasInsufficientSnodes
|
||||
}
|
||||
|
@ -150,7 +169,7 @@ public final class SnodeAPI {
|
|||
public static func getSnodePool(
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Set<Snode>, Error> {
|
||||
loadSnodePoolIfNeeded()
|
||||
loadSnodePoolIfNeeded(using: dependencies)
|
||||
|
||||
let now: Date = Date()
|
||||
let hasSnodePoolExpired: Bool = dependencies.storage[.lastSnodePoolRefreshDate]
|
||||
|
@ -190,10 +209,10 @@ public final class SnodeAPI {
|
|||
.tryFlatMap { snodePool -> AnyPublisher<Set<Snode>, Error> in
|
||||
guard !snodePool.isEmpty else { throw SnodeAPIError.snodePoolUpdatingFailed }
|
||||
|
||||
return Storage.shared
|
||||
return dependencies.storage
|
||||
.writePublisher { db in
|
||||
db[.lastSnodePoolRefreshDate] = now
|
||||
setSnodePool(db, to: snodePool)
|
||||
setSnodePool(db, to: snodePool, using: dependencies)
|
||||
|
||||
return snodePool
|
||||
}
|
||||
|
@ -238,35 +257,35 @@ public final class SnodeAPI {
|
|||
(0..<validationCount)
|
||||
.map { _ in
|
||||
SnodeAPI
|
||||
.getRandomSnode()
|
||||
.flatMap { snode -> AnyPublisher<String, Error> in
|
||||
SnodeAPI
|
||||
.send(
|
||||
request: SnodeRequest(
|
||||
endpoint: .oxenDaemonRPCCall,
|
||||
body: OxenDaemonRPCRequest(
|
||||
endpoint: .daemonOnsResolve,
|
||||
body: ONSResolveRequest(
|
||||
type: 0, // type 0 means Session
|
||||
base64EncodedNameHash: base64EncodedNameHash
|
||||
)
|
||||
.getRandomSnode(using: dependencies)
|
||||
.flatMap { snode -> AnyPublisher<String, Error> in
|
||||
SnodeAPI
|
||||
.send(
|
||||
request: SnodeRequest(
|
||||
endpoint: .oxenDaemonRPCCall,
|
||||
body: OxenDaemonRPCRequest(
|
||||
endpoint: .daemonOnsResolve,
|
||||
body: ONSResolveRequest(
|
||||
type: 0, // type 0 means Session
|
||||
base64EncodedNameHash: base64EncodedNameHash
|
||||
)
|
||||
),
|
||||
to: snode,
|
||||
associatedWith: nil,
|
||||
using: dependencies
|
||||
)
|
||||
.decoded(as: ONSResolveResponse.self)
|
||||
.tryMap { _, response -> String in
|
||||
try response.sessionId(
|
||||
sodium: sodium.wrappedValue,
|
||||
nameBytes: nameAsData,
|
||||
nameHashBytes: nameHash
|
||||
)
|
||||
}
|
||||
.retry(4)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
),
|
||||
to: snode,
|
||||
associatedWith: nil,
|
||||
using: dependencies
|
||||
)
|
||||
.decoded(as: ONSResolveResponse.self)
|
||||
.tryMap { _, response -> String in
|
||||
try response.sessionId(
|
||||
sodium: sodium.wrappedValue,
|
||||
nameBytes: nameAsData,
|
||||
nameHashBytes: nameHash
|
||||
)
|
||||
}
|
||||
.retry(4)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
)
|
||||
.collect()
|
||||
|
@ -284,7 +303,7 @@ public final class SnodeAPI {
|
|||
for publicKey: String,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> AnyPublisher<Set<Snode>, Error> {
|
||||
loadSwarmIfNeeded(for: publicKey)
|
||||
loadSwarmIfNeeded(for: publicKey, using: dependencies)
|
||||
|
||||
if let cachedSwarm = swarmCache.wrappedValue[publicKey], cachedSwarm.count >= minSwarmSnodeCount {
|
||||
return Just(cachedSwarm)
|
||||
|
@ -292,9 +311,10 @@ public final class SnodeAPI {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
SNLog("Getting swarm for: \((publicKey == getUserHexEncodedPublicKey()) ? "self" : publicKey).")
|
||||
let currentUserPublicKey: String = getUserHexEncodedPublicKey(using: dependencies)
|
||||
SNLog("Getting swarm for: \((publicKey == currentUserPublicKey) ? "self" : publicKey).")
|
||||
|
||||
return getRandomSnode()
|
||||
return getRandomSnode(using: dependencies)
|
||||
.flatMap { snode in
|
||||
SnodeAPI.send(
|
||||
request: SnodeRequest(
|
||||
|
@ -310,7 +330,7 @@ public final class SnodeAPI {
|
|||
}
|
||||
.map { _, responseData in parseSnodes(from: responseData) }
|
||||
.handleEvents(
|
||||
receiveOutput: { swarm in setSwarm(to: swarm, for: publicKey) }
|
||||
receiveOutput: { swarm in setSwarm(to: swarm, for: publicKey, using: dependencies) }
|
||||
)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
@ -1083,9 +1103,11 @@ public final class SnodeAPI {
|
|||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
internal static func getRandomSnode() -> AnyPublisher<Snode, Error> {
|
||||
internal static func getRandomSnode(
|
||||
using dependencies: Dependencies
|
||||
) -> AnyPublisher<Snode, Error> {
|
||||
// randomElement() uses the system's default random generator, which is cryptographically secure
|
||||
return getSnodePool()
|
||||
return getSnodePool(using: dependencies)
|
||||
.map { $0.randomElement()! }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
@ -1246,7 +1268,15 @@ public final class SnodeAPI {
|
|||
.mapError { error in
|
||||
switch error {
|
||||
case HTTPError.httpRequestFailed(let statusCode, let data):
|
||||
return (SnodeAPI.handleError(withStatusCode: statusCode, data: data, forSnode: snode, associatedWith: publicKey) ?? error)
|
||||
return SnodeAPI
|
||||
.handleError(
|
||||
withStatusCode: statusCode,
|
||||
data: data,
|
||||
forSnode: snode,
|
||||
associatedWith: publicKey,
|
||||
using: dependencies
|
||||
)
|
||||
.defaulting(to: error)
|
||||
|
||||
default: return error
|
||||
}
|
||||
|
@ -1259,7 +1289,15 @@ public final class SnodeAPI {
|
|||
.mapError { error in
|
||||
switch error {
|
||||
case HTTPError.httpRequestFailed(let statusCode, let data):
|
||||
return (SnodeAPI.handleError(withStatusCode: statusCode, data: data, forSnode: snode, associatedWith: publicKey) ?? error)
|
||||
return SnodeAPI
|
||||
.handleError(
|
||||
withStatusCode: statusCode,
|
||||
data: data,
|
||||
forSnode: snode,
|
||||
associatedWith: publicKey,
|
||||
using: dependencies
|
||||
)
|
||||
.defaulting(to: error)
|
||||
|
||||
default: return error
|
||||
}
|
||||
|
@ -1334,9 +1372,10 @@ public final class SnodeAPI {
|
|||
withStatusCode statusCode: UInt,
|
||||
data: Data?,
|
||||
forSnode snode: Snode,
|
||||
associatedWith publicKey: String? = nil
|
||||
associatedWith publicKey: String? = nil,
|
||||
using dependencies: Dependencies
|
||||
) -> Error? {
|
||||
func handleBadSnode() {
|
||||
func handleBadSnode(using dependencies: Dependencies) {
|
||||
let oldFailureCount = (SnodeAPI.snodeFailureCount.wrappedValue[snode] ?? 0)
|
||||
let newFailureCount = oldFailureCount + 1
|
||||
SnodeAPI.snodeFailureCount.mutate { $0[snode] = newFailureCount }
|
||||
|
@ -1344,7 +1383,7 @@ public final class SnodeAPI {
|
|||
if newFailureCount >= SnodeAPI.snodeFailureThreshold {
|
||||
SNLog("Failure threshold reached for: \(snode); dropping it.")
|
||||
if let publicKey = publicKey {
|
||||
SnodeAPI.dropSnodeFromSwarmIfNeeded(snode, publicKey: publicKey)
|
||||
SnodeAPI.dropSnodeFromSwarmIfNeeded(snode, publicKey: publicKey, using: dependencies)
|
||||
}
|
||||
SnodeAPI.dropSnodeFromSnodePool(snode)
|
||||
SNLog("Snode pool count: \(snodePool.wrappedValue.count).")
|
||||
|
@ -1355,7 +1394,7 @@ public final class SnodeAPI {
|
|||
switch statusCode {
|
||||
case 500, 502, 503:
|
||||
// The snode is unreachable
|
||||
handleBadSnode()
|
||||
handleBadSnode(using: dependencies)
|
||||
|
||||
case 404:
|
||||
// May caused by invalid open groups
|
||||
|
@ -1370,14 +1409,14 @@ public final class SnodeAPI {
|
|||
if let publicKey = publicKey {
|
||||
func invalidateSwarm() {
|
||||
SNLog("Invalidating swarm for: \(publicKey).")
|
||||
SnodeAPI.dropSnodeFromSwarmIfNeeded(snode, publicKey: publicKey)
|
||||
SnodeAPI.dropSnodeFromSwarmIfNeeded(snode, publicKey: publicKey, using: dependencies)
|
||||
}
|
||||
|
||||
if let data: Data = data {
|
||||
let snodes = parseSnodes(from: data)
|
||||
|
||||
if !snodes.isEmpty {
|
||||
setSwarm(to: snodes, for: publicKey)
|
||||
setSwarm(to: snodes, for: publicKey, using: dependencies)
|
||||
}
|
||||
else {
|
||||
invalidateSwarm()
|
||||
|
@ -1392,7 +1431,7 @@ public final class SnodeAPI {
|
|||
}
|
||||
|
||||
default:
|
||||
handleBadSnode()
|
||||
handleBadSnode(using: dependencies)
|
||||
let message: String = {
|
||||
if let data: Data = data, let stringFromData = String(data: data, encoding: .utf8) {
|
||||
return stringFromData
|
||||
|
|
|
@ -12,7 +12,7 @@ enum _001_ThemePreferences: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
// Determine if the user was matching the system setting (previously the absence of this value
|
||||
// indicated that the app should match the system setting)
|
||||
let isExistingUser: Bool = Identity.userExists(db)
|
||||
|
|
|
@ -9,7 +9,7 @@ enum _001_InitialSetupMigration: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.create(table: Identity.self) { t in
|
||||
t.column(.variant, .text)
|
||||
.notNull()
|
||||
|
|
|
@ -11,7 +11,7 @@ enum _002_SetupStandardJobs: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try autoreleasepool {
|
||||
// Note: This job exists in the 'Session' target but that doesn't have it's own migrations
|
||||
_ = try Job(
|
||||
|
|
|
@ -9,7 +9,7 @@ enum _003_YDBToGRDBMigration: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
guard !SNUtilitiesKit.isRunningTests else { return Storage.update(progress: 1, for: self, in: target) }
|
||||
|
||||
SNLogNotTests("[Migration Error] Attempted to perform legacy migation")
|
||||
|
|
|
@ -9,7 +9,7 @@ enum _004_AddJobPriority: Migration {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
// Add `priority` to the job table
|
||||
try db.alter(table: Job.self) { t in
|
||||
t.add(.priority, .integer).defaults(to: 0)
|
||||
|
|
|
@ -74,33 +74,45 @@ public extension Identity {
|
|||
try Identity(variant: .x25519PublicKey, data: Data(x25519KeyPair.publicKey)).save(db)
|
||||
}
|
||||
|
||||
static func userExists(_ db: Database? = nil) -> Bool {
|
||||
return (fetchUserKeyPair(db) != nil)
|
||||
static func userExists(
|
||||
_ db: Database? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Bool {
|
||||
return (fetchUserKeyPair(db, using: dependencies) != nil)
|
||||
}
|
||||
|
||||
static func fetchUserPublicKey(_ db: Database? = nil) -> Data? {
|
||||
static func fetchUserPublicKey(
|
||||
_ db: Database? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Data? {
|
||||
guard let db: Database = db else {
|
||||
return Storage.shared.read { db in fetchUserPublicKey(db) }
|
||||
return dependencies.storage.read { db in fetchUserPublicKey(db, using: dependencies) }
|
||||
}
|
||||
|
||||
return try? Identity.fetchOne(db, id: .x25519PublicKey)?.data
|
||||
}
|
||||
|
||||
static func fetchUserPrivateKey(_ db: Database? = nil) -> Data? {
|
||||
static func fetchUserPrivateKey(
|
||||
_ db: Database? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> Data? {
|
||||
guard let db: Database = db else {
|
||||
return Storage.shared.read { db in fetchUserPrivateKey(db) }
|
||||
return dependencies.storage.read { db in fetchUserPrivateKey(db, using: dependencies) }
|
||||
}
|
||||
|
||||
return try? Identity.fetchOne(db, id: .x25519PrivateKey)?.data
|
||||
}
|
||||
|
||||
static func fetchUserKeyPair(_ db: Database? = nil) -> KeyPair? {
|
||||
static func fetchUserKeyPair(
|
||||
_ db: Database? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> KeyPair? {
|
||||
guard let db: Database = db else {
|
||||
return Storage.shared.read { db in fetchUserKeyPair(db) }
|
||||
return dependencies.storage.read { db in fetchUserKeyPair(db, using: dependencies) }
|
||||
}
|
||||
guard
|
||||
let publicKey: Data = fetchUserPublicKey(db),
|
||||
let privateKey: Data = fetchUserPrivateKey(db)
|
||||
let publicKey: Data = fetchUserPublicKey(db, using: dependencies),
|
||||
let privateKey: Data = fetchUserPrivateKey(db, using: dependencies)
|
||||
else { return nil }
|
||||
|
||||
return KeyPair(
|
||||
|
@ -109,9 +121,12 @@ public extension Identity {
|
|||
)
|
||||
}
|
||||
|
||||
static func fetchUserEd25519KeyPair(_ db: Database? = nil) -> KeyPair? {
|
||||
static func fetchUserEd25519KeyPair(
|
||||
_ db: Database? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) -> KeyPair? {
|
||||
guard let db: Database = db else {
|
||||
return Storage.shared.read { db in fetchUserEd25519KeyPair(db) }
|
||||
return dependencies.storage.read { db in fetchUserEd25519KeyPair(db, using: dependencies) }
|
||||
}
|
||||
guard
|
||||
let publicKey: Data = try? Identity.fetchOne(db, id: .ed25519PublicKey)?.data,
|
||||
|
|
|
@ -56,14 +56,20 @@ open class Storage {
|
|||
|
||||
public init(
|
||||
customWriter: DatabaseWriter? = nil,
|
||||
customMigrationTargets: [MigratableTarget.Type]? = nil
|
||||
customMigrationTargets: [MigratableTarget.Type]? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
configureDatabase(customWriter: customWriter, customMigrationTargets: customMigrationTargets)
|
||||
configureDatabase(
|
||||
customWriter: customWriter,
|
||||
customMigrationTargets: customMigrationTargets,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
private func configureDatabase(
|
||||
customWriter: DatabaseWriter? = nil,
|
||||
customMigrationTargets: [MigratableTarget.Type]? = nil
|
||||
customMigrationTargets: [MigratableTarget.Type]? = nil,
|
||||
using dependencies: Dependencies = Dependencies()
|
||||
) {
|
||||
// Create the database directory if needed and ensure it's protection level is set before attempting to
|
||||
// create the database KeySpec or the database itself
|
||||
|
@ -80,7 +86,8 @@ open class Storage {
|
|||
async: false,
|
||||
onProgressUpdate: nil,
|
||||
onMigrationRequirement: { _, _ in },
|
||||
onComplete: { _, _ in }
|
||||
onComplete: { _, _ in },
|
||||
using: dependencies
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -152,7 +159,8 @@ open class Storage {
|
|||
async: Bool = true,
|
||||
onProgressUpdate: ((CGFloat, TimeInterval) -> ())?,
|
||||
onMigrationRequirement: @escaping (Database, MigrationRequirement) -> (),
|
||||
onComplete: @escaping (Swift.Result<Void, Error>, Bool) -> ()
|
||||
onComplete: @escaping (Swift.Result<Void, Error>, Bool) -> (),
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
guard isValid, let dbWriter: DatabaseWriter = dbWriter else {
|
||||
let error: Error = (startupError ?? StorageError.startupFailed)
|
||||
|
@ -186,11 +194,16 @@ open class Storage {
|
|||
}
|
||||
|
||||
// Setup and run any required migrations
|
||||
migrator = { [weak self] in
|
||||
migrator = { [weak self, dependencies] in
|
||||
var migrator: DatabaseMigrator = DatabaseMigrator()
|
||||
sortedMigrationInfo.forEach { migrationInfo in
|
||||
migrationInfo.migrations.forEach { migration in
|
||||
migrator.registerMigration(self, targetIdentifier: migrationInfo.identifier, migration: migration)
|
||||
migrator.registerMigration(
|
||||
self,
|
||||
targetIdentifier: migrationInfo.identifier,
|
||||
migration: migration,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ public protocol Migration {
|
|||
static var minExpectedRunDuration: TimeInterval { get }
|
||||
static var requirements: [MigrationRequirement] { get }
|
||||
|
||||
static func migrate(_ db: Database) throws
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws
|
||||
}
|
||||
|
||||
public extension Migration {
|
||||
|
@ -18,7 +18,8 @@ public extension Migration {
|
|||
|
||||
static func loggedMigrate(
|
||||
_ storage: Storage?,
|
||||
targetIdentifier: TargetMigrations.Identifier
|
||||
targetIdentifier: TargetMigrations.Identifier,
|
||||
using dependencies: Dependencies
|
||||
) -> ((_ db: Database) throws -> ()) {
|
||||
return { (db: Database) in
|
||||
SNLogNotTests("[Migration Info] Starting \(targetIdentifier.key(with: self))")
|
||||
|
@ -26,7 +27,7 @@ public extension Migration {
|
|||
storage?.internalCurrentlyRunningMigration.mutate { $0 = (targetIdentifier, self) }
|
||||
defer { storage?.internalCurrentlyRunningMigration.mutate { $0 = nil } }
|
||||
|
||||
try migrate(db)
|
||||
try migrate(db, using: dependencies)
|
||||
SNLogNotTests("[Migration Info] Completed \(targetIdentifier.key(with: self))")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,12 @@ public extension DatabaseMigrator {
|
|||
_ storage: Storage?,
|
||||
targetIdentifier: TargetMigrations.Identifier,
|
||||
migration: Migration.Type,
|
||||
foreignKeyChecks: ForeignKeyChecks = .deferred
|
||||
foreignKeyChecks: ForeignKeyChecks = .deferred,
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
self.registerMigration(
|
||||
targetIdentifier.key(with: migration),
|
||||
migrate: migration.loggedMigrate(storage, targetIdentifier: targetIdentifier)
|
||||
migrate: migration.loggedMigrate(storage, targetIdentifier: targetIdentifier, using: dependencies)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,8 @@ public enum GeneralError: Error {
|
|||
public func getUserHexEncodedPublicKey(_ db: Database? = nil, using dependencies: Dependencies = Dependencies()) -> String {
|
||||
if let cachedKey: String = dependencies.caches[.general].encodedPublicKey { return cachedKey }
|
||||
|
||||
if let publicKey: Data = Identity.fetchUserPublicKey(db) { // Can be nil under some circumstances
|
||||
// Can be nil under some circumstances
|
||||
if let publicKey: Data = Identity.fetchUserPublicKey(db, using: dependencies) {
|
||||
let sessionId: SessionId = SessionId(.standard, publicKey: publicKey.bytes)
|
||||
|
||||
dependencies.caches.mutate(cache: .general) { $0.encodedPublicKey = sessionId.hexString }
|
||||
|
|
|
@ -53,7 +53,7 @@ class PersistableRecordUtilitiesSpec: QuickSpec {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.create(table: TestType.self) { t in
|
||||
t.column(.columnA, .text).primaryKey()
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ class PersistableRecordUtilitiesSpec: QuickSpec {
|
|||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
|
||||
try db.alter(table: TestType.self) { t in
|
||||
t.add(.columnB, .text)
|
||||
}
|
||||
|
@ -98,6 +98,7 @@ class PersistableRecordUtilitiesSpec: QuickSpec {
|
|||
override func spec() {
|
||||
var customWriter: DatabaseQueue!
|
||||
var mockStorage: Storage!
|
||||
var dependencies: Dependencies!
|
||||
|
||||
describe("a PersistableRecord") {
|
||||
beforeEach {
|
||||
|
@ -108,6 +109,9 @@ class PersistableRecordUtilitiesSpec: QuickSpec {
|
|||
TestTarget.self
|
||||
]
|
||||
)
|
||||
dependencies = Dependencies(
|
||||
storage: mockStorage
|
||||
)
|
||||
}
|
||||
|
||||
afterEach {
|
||||
|
@ -345,7 +349,8 @@ class PersistableRecordUtilitiesSpec: QuickSpec {
|
|||
migrator.registerMigration(
|
||||
mockStorage,
|
||||
targetIdentifier: TestAddColumnMigration.target,
|
||||
migration: TestAddColumnMigration.self
|
||||
migration: TestAddColumnMigration.self,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
expect { try migrator.migrate(customWriter) }
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue