[WIP] Refactored dependency access to be more generic

This commit is contained in:
Morgan Pretty 2023-09-01 13:40:50 +10:00
parent 6dd4c797a7
commit c77d7ecda1
175 changed files with 1745 additions and 1337 deletions

@ -1 +1 @@
Subproject commit 517a61a455d31cd9363198d1b3d02f20093a5811
Subproject commit 9b0cdcdc0cfc788b4e46c1b337ceddfbc1deee0f

View File

@ -530,10 +530,7 @@
FD23CE282A67755C0000B97C /* MockCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE272A67755C0000B97C /* MockCrypto.swift */; };
FD23CE292A6775650000B97C /* MockCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE272A67755C0000B97C /* MockCrypto.swift */; };
FD23CE2A2A6775660000B97C /* MockCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE272A67755C0000B97C /* MockCrypto.swift */; };
FD23CE2C2A678DF80000B97C /* MockCaches.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE2B2A678DF80000B97C /* MockCaches.swift */; };
FD23CE2D2A678E1E0000B97C /* MockCaches.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE2B2A678DF80000B97C /* MockCaches.swift */; };
FD23CE2E2A678E1E0000B97C /* MockCaches.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE2B2A678DF80000B97C /* MockCaches.swift */; };
FD23CE302A67B8820000B97C /* Caches.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE2F2A67B8820000B97C /* Caches.swift */; };
FD23CE302A67B8820000B97C /* CacheInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE2F2A67B8820000B97C /* CacheInfo.swift */; };
FD23CE332A67C4D90000B97C /* MockNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE312A67C38D0000B97C /* MockNetwork.swift */; };
FD23CE342A67C4D90000B97C /* MockNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE312A67C38D0000B97C /* MockNetwork.swift */; };
FD23CE352A67C4DA0000B97C /* MockNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE312A67C38D0000B97C /* MockNetwork.swift */; };
@ -649,6 +646,10 @@
FD5C7307284F103B0029977D /* MessageReceiver+MessageRequests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5C7306284F103B0029977D /* MessageReceiver+MessageRequests.swift */; };
FD5C7309285007920029977D /* BlindedIdLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5C7308285007920029977D /* BlindedIdLookup.swift */; };
FD5D201E27B0D87C00FEA984 /* SessionId.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201D27B0D87C00FEA984 /* SessionId.swift */; };
FD65318A2AA025C500DFEEAA /* TestDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6531892AA025C500DFEEAA /* TestDependencies.swift */; };
FD65318B2AA025C500DFEEAA /* TestDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6531892AA025C500DFEEAA /* TestDependencies.swift */; };
FD65318C2AA025C500DFEEAA /* TestDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6531892AA025C500DFEEAA /* TestDependencies.swift */; };
FD65318D2AA025C500DFEEAA /* TestDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6531892AA025C500DFEEAA /* TestDependencies.swift */; };
FD6A7A692818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A682818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift */; };
FD6A7A6B2818C17C00035AC1 /* UpdateProfilePictureJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A6A2818C17C00035AC1 /* UpdateProfilePictureJob.swift */; };
FD6A7A6D2818C61500035AC1 /* _002_SetupStandardJobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6A7A6C2818C61500035AC1 /* _002_SetupStandardJobs.swift */; };
@ -786,7 +787,6 @@
FDB5DB072A981F88002C8721 /* Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC290A527D860CE005DAE71 /* Mock.swift */; };
FDB5DB082A981F8B002C8721 /* Mocked.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0969F82A69FFE700C5C365 /* Mocked.swift */; };
FDB5DB092A981F8D002C8721 /* MockCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE272A67755C0000B97C /* MockCrypto.swift */; };
FDB5DB0A2A981F90002C8721 /* MockCaches.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE2B2A678DF80000B97C /* MockCaches.swift */; };
FDB5DB0B2A981F92002C8721 /* MockGeneralCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */; };
FDB5DB0C2A981F96002C8721 /* MockNetwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD23CE312A67C38D0000B97C /* MockNetwork.swift */; };
FDB5DB0D2A981F9D002C8721 /* MockJobRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD96F3A629DBD43D00401309 /* MockJobRunner.swift */; };
@ -885,6 +885,7 @@
FDE77F6B280FEB28002CFC5D /* ControlMessageProcessRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */; };
FDED2E3C282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDED2E3B282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift */; };
FDF01FAB2A9EBAD500CAF969 /* MessageSenderGroupsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF01FAA2A9EBAD500CAF969 /* MessageSenderGroupsSpec.swift */; };
FDF01FAD2A9ECC4200CAF969 /* SingletonInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF01FAC2A9ECC4200CAF969 /* SingletonInfo.swift */; };
FDF0B73C27FFD3D6004C14C5 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */; };
FDF0B7422804EA4F004C14C5 /* _002_SetupStandardJobs.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7412804EA4F004C14C5 /* _002_SetupStandardJobs.swift */; };
FDF0B7472804F0CE004C14C5 /* DisappearingMessagesJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B7462804F0CE004C14C5 /* DisappearingMessagesJob.swift */; };
@ -1757,8 +1758,7 @@
FD23CE232A675C440000B97C /* Crypto+SessionMessagingKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Crypto+SessionMessagingKit.swift"; sourceTree = "<group>"; };
FD23CE252A676B5B0000B97C /* DependenciesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DependenciesSpec.swift; sourceTree = "<group>"; };
FD23CE272A67755C0000B97C /* MockCrypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCrypto.swift; sourceTree = "<group>"; };
FD23CE2B2A678DF80000B97C /* MockCaches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCaches.swift; sourceTree = "<group>"; };
FD23CE2F2A67B8820000B97C /* Caches.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Caches.swift; sourceTree = "<group>"; };
FD23CE2F2A67B8820000B97C /* CacheInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheInfo.swift; sourceTree = "<group>"; };
FD23CE312A67C38D0000B97C /* MockNetwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNetwork.swift; sourceTree = "<group>"; };
FD23EA6028ED0B260058676E /* CombineExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineExtensions.swift; sourceTree = "<group>"; };
FD245C612850664300B966DD /* Configuration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
@ -1841,6 +1841,7 @@
FD5C7308285007920029977D /* BlindedIdLookup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlindedIdLookup.swift; sourceTree = "<group>"; };
FD5CE3442A3C5D96001A6DE3 /* DecryptExportedKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecryptExportedKey.swift; sourceTree = "<group>"; };
FD5D201D27B0D87C00FEA984 /* SessionId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionId.swift; sourceTree = "<group>"; };
FD6531892AA025C500DFEEAA /* TestDependencies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestDependencies.swift; sourceTree = "<group>"; };
FD6A7A682818BE7300035AC1 /* RetrieveDefaultOpenGroupRoomsJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RetrieveDefaultOpenGroupRoomsJob.swift; sourceTree = "<group>"; };
FD6A7A6A2818C17C00035AC1 /* UpdateProfilePictureJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateProfilePictureJob.swift; sourceTree = "<group>"; };
FD6A7A6C2818C61500035AC1 /* _002_SetupStandardJobs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _002_SetupStandardJobs.swift; sourceTree = "<group>"; };
@ -2053,6 +2054,7 @@
FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlMessageProcessRecord.swift; sourceTree = "<group>"; };
FDED2E3B282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+ReusableView.swift"; sourceTree = "<group>"; };
FDF01FAA2A9EBAD500CAF969 /* MessageSenderGroupsSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSenderGroupsSpec.swift; sourceTree = "<group>"; };
FDF01FAC2A9ECC4200CAF969 /* SingletonInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingletonInfo.swift; sourceTree = "<group>"; };
FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkPreview.swift; sourceTree = "<group>"; };
FDF0B73F280402C4004C14C5 /* Job.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Job.swift; sourceTree = "<group>"; };
FDF0B7412804EA4F004C14C5 /* _002_SetupStandardJobs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _002_SetupStandardJobs.swift; sourceTree = "<group>"; };
@ -4160,11 +4162,11 @@
FDC290A527D860CE005DAE71 /* Mock.swift */,
FD0969F82A69FFE700C5C365 /* Mocked.swift */,
FD23CE272A67755C0000B97C /* MockCrypto.swift */,
FD23CE2B2A678DF80000B97C /* MockCaches.swift */,
FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */,
FD23CE312A67C38D0000B97C /* MockNetwork.swift */,
FD96F3A629DBD43D00401309 /* MockJobRunner.swift */,
FD83B9BD27CF2243005E1583 /* TestConstants.swift */,
FD6531892AA025C500DFEEAA /* TestDependencies.swift */,
FD9DD2702A72516D00ECB68E /* TestExtensions.swift */,
FDC290A727D9B46D005DAE71 /* NimbleExtensions.swift */,
FD078E4727E02561000769AF /* CommonMockedExtensions.swift */,
@ -4496,8 +4498,9 @@
FDF01FAE2A9ED0C800CAF969 /* Dependency Injection */ = {
isa = PBXGroup;
children = (
FD23CE2F2A67B8820000B97C /* Caches.swift */,
FD23CE2F2A67B8820000B97C /* CacheInfo.swift */,
FDC6D75F2862B3F600B04575 /* Dependencies.swift */,
FDF01FAC2A9ECC4200CAF969 /* SingletonInfo.swift */,
);
path = "Dependency Injection";
sourceTree = "<group>";
@ -5982,7 +5985,7 @@
FD23CE1F2A65269C0000B97C /* Crypto.swift in Sources */,
B8BC00C0257D90E30032E807 /* General.swift in Sources */,
FDF8488629405A61007DCAE5 /* Request.swift in Sources */,
FD23CE302A67B8820000B97C /* Caches.swift in Sources */,
FD23CE302A67B8820000B97C /* CacheInfo.swift in Sources */,
FD17D7A127F40D2500122BE0 /* Storage.swift in Sources */,
FD1A94FB2900D1C2000D73D3 /* PersistableRecord+Utilities.swift in Sources */,
FD5D201E27B0D87C00FEA984 /* SessionId.swift in Sources */,
@ -6022,6 +6025,7 @@
FDF8488929405B27007DCAE5 /* Data+Utilities.swift in Sources */,
FD09797227FAA2F500936362 /* Optional+Utilities.swift in Sources */,
FD7162DB281B6C440060647B /* TypedTableAlias.swift in Sources */,
FDF01FAD2A9ECC4200CAF969 /* SingletonInfo.swift in Sources */,
FD7115F828C8151C00B47552 /* DisposableBarButtonItem.swift in Sources */,
FD17D7E727F6A16700122BE0 /* _003_YDBToGRDBMigration.swift in Sources */,
);
@ -6233,8 +6237,6 @@
FD245C56285065EA00B966DD /* SNProto.swift in Sources */,
FD09798B27FD1CFE00936362 /* Capability.swift in Sources */,
C3BBE0C72554F1570050F1E3 /* FixedWidthInteger+BigEndian.swift in Sources */,
FD1D732A2A85AA2000E3F410 /* Setting+Utilities.swift in Sources */,
FD17D79C27F40B2E00122BE0 /* SMKLegacy.swift in Sources */,
FD09798127FCFEE800936362 /* SessionThread.swift in Sources */,
FD09C5EA282A1BB2000CE219 /* ThreadTypingIndicator.swift in Sources */,
FDB5DADA2A95D839002C8721 /* GroupUpdateInfoChangeMessage.swift in Sources */,
@ -6472,7 +6474,6 @@
FD23CE292A6775650000B97C /* MockCrypto.swift in Sources */,
FD23CE332A67C4D90000B97C /* MockNetwork.swift in Sources */,
FD71161528D00D6700B47552 /* ThreadDisappearingMessagesViewModelSpec.swift in Sources */,
FD23CE2D2A678E1E0000B97C /* MockCaches.swift in Sources */,
FD23EA5E28ED00FD0058676E /* NimbleExtensions.swift in Sources */,
FD23EA5F28ED00FF0058676E /* CommonMockedExtensions.swift in Sources */,
FD23EA5D28ED00FA0058676E /* TestConstants.swift in Sources */,
@ -6482,6 +6483,7 @@
FD23EA6128ED0B260058676E /* CombineExtensions.swift in Sources */,
7B6ACADE2A32D3A9009AFB73 /* MessageReceiverDisappearingMessagesSpec.swift in Sources */,
FD9DD2712A72516D00ECB68E /* TestExtensions.swift in Sources */,
FD65318A2AA025C500DFEEAA /* TestDependencies.swift in Sources */,
FD2AAAED28ED3E1000A49611 /* MockGeneralCache.swift in Sources */,
FDB5DB0D2A981F9D002C8721 /* MockJobRunner.swift in Sources */,
);
@ -6493,6 +6495,7 @@
files = (
FD2AAAF228ED57B500A49611 /* SynchronousStorage.swift in Sources */,
FD078E4927E02576000769AF /* CommonMockedExtensions.swift in Sources */,
FD65318D2AA025C500DFEEAA /* TestDependencies.swift in Sources */,
FD83B9BF27CF2294005E1583 /* TestConstants.swift in Sources */,
FDFBB7542A2023EB00CA7350 /* BencodeSpec.swift in Sources */,
FD9DD2732A72516D00ECB68E /* TestExtensions.swift in Sources */,
@ -6506,7 +6509,6 @@
FD2AAAEE28ED3E1100A49611 /* MockGeneralCache.swift in Sources */,
FD9B30F3293EA0BF008DEE3E /* BatchResponseSpec.swift in Sources */,
FD37EA1528AB42CB003AE748 /* IdentitySpec.swift in Sources */,
FD23CE2C2A678DF80000B97C /* MockCaches.swift in Sources */,
FD0B77B229B82B7A009169BA /* ArrayUtilitiesSpec.swift in Sources */,
FD1A94FE2900D2EA000D73D3 /* PersistableRecordUtilitiesSpec.swift in Sources */,
FD23CE262A676B5B0000B97C /* DependenciesSpec.swift in Sources */,
@ -6526,9 +6528,9 @@
FDB5DB082A981F8B002C8721 /* Mocked.swift in Sources */,
FDB5DB142A981FAE002C8721 /* CombineExtensions.swift in Sources */,
FDB5DB162A9821DF002C8721 /* CommonMockedExtensions.swift in Sources */,
FDB5DB0A2A981F90002C8721 /* MockCaches.swift in Sources */,
FDB5DB112A981FA6002C8721 /* TestExtensions.swift in Sources */,
FDB5DB092A981F8D002C8721 /* MockCrypto.swift in Sources */,
FD65318C2AA025C500DFEEAA /* TestDependencies.swift in Sources */,
FDB5DB102A981FA3002C8721 /* TestConstants.swift in Sources */,
FDB5DB0C2A981F96002C8721 /* MockNetwork.swift in Sources */,
FDB5DB0F2A981FA1002C8721 /* MockJobRunner.swift in Sources */,
@ -6570,8 +6572,8 @@
FD2B4AFB29429D1000AB4848 /* ConfigContacts.swift in Sources */,
FDA1E83D29AC71A800C5C3BD /* SessionUtilSpec.swift in Sources */,
FD83B9C027CF2294005E1583 /* TestConstants.swift in Sources */,
FD65318B2AA025C500DFEEAA /* TestDependencies.swift in Sources */,
FDC4389A27BA002500C60D73 /* OpenGroupAPISpec.swift in Sources */,
FD23CE2E2A678E1E0000B97C /* MockCaches.swift in Sources */,
FD23EA6228ED0B260058676E /* CombineExtensions.swift in Sources */,
FDC3833B2A9344C700FFD6A2 /* ConfigGroupInfo.swift in Sources */,
FDB5DACD2A9452F0002C8721 /* ConfigGroupMembers.swift in Sources */,

View File

@ -274,7 +274,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
webRTCSession.hangUp()
Storage.shared.writeAsync { [weak self] db in
Dependencies()[singleton: .storage].writeAsync { [weak self] db in
try self?.webRTCSession.endCall(db, with: sessionId)
}
@ -289,7 +289,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
let duration: TimeInterval = self.duration
let hasStartedConnecting: Bool = self.hasStartedConnecting
dependencies.storage.writeAsync(
dependencies[singleton: .storage].writeAsync(
updates: { db in
guard let interaction: Interaction = try? Interaction.fetchOne(db, id: callInteractionId) else {
return
@ -430,7 +430,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
let sessionId: String = self.sessionId
let webRTCSession: WebRTCSession = self.webRTCSession
guard let thread: SessionThread = Storage.shared.read({ db in try SessionThread.fetchOne(db, id: sessionId) }) else {
guard let thread: SessionThread = Dependencies()[singleton: .storage].read({ db in try SessionThread.fetchOne(db, id: sessionId) }) else {
return
}

View File

@ -9,7 +9,7 @@ extension SessionCallManager {
public func startCallAction() -> Bool {
guard let call: CurrentCallProtocol = self.currentCall else { return false }
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
call.startSessionCall(db)
}

View File

@ -188,10 +188,12 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
callUpdate.supportsDTMF = false
}
public static func suspendDatabaseIfCallEndedInBackground() {
public static func suspendDatabaseIfCallEndedInBackground(
using dependencies: Dependencies = Dependencies()
) {
if CurrentAppContext().isInBackground() {
// Stop all jobs except for message sending and when completed suspend the database
JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend) {
dependencies[singleton: .jobRunner].stopAndClearPendingJobs(exceptForVariant: .messageSend) {
Storage.suspendDatabaseAccess()
}
}
@ -200,7 +202,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
// MARK: - UI
public func showCallUIForCall(caller: String, uuid: String, mode: CallMode, interactionId: Int64?) {
guard let call: SessionCall = Storage.shared.read({ db in SessionCall(db, for: caller, uuid: uuid, mode: mode) }) else {
guard let call: SessionCall = Dependencies()[singleton: .storage].read({ db in SessionCall(db, for: caller, uuid: uuid, mode: mode) }) else {
return
}

View File

@ -114,7 +114,7 @@ final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate {
publicKey: call.sessionId,
threadVariant: .contact,
customImageData: nil,
profile: Storage.shared.read { db in Profile.fetchOrCreate(db, id: call.sessionId) },
profile: Dependencies()[singleton: .storage].read { db in Profile.fetchOrCreate(db, id: call.sessionId) },
additionalProfile: nil
)
displayNameLabel.text = call.contactName

View File

@ -102,7 +102,7 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat
let threadId: String = self.threadId
Storage.shared.read { [weak self] db in
Dependencies()[singleton: .storage].read { [weak self] db in
let userPublicKey: String = getUserHexEncodedPublicKey(db)
self?.userPublicKey = userPublicKey
self?.name = try ClosedGroup
@ -368,7 +368,7 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat
.map { $0.profileId }
.asSet()
) { [weak self] selectedUserIds in
Storage.shared.read { [weak self] db in
Dependencies()[singleton: .storage].read { [weak self] db in
let selectedGroupMembers: [GroupMemberDisplayInfo] = try Profile
.filter(selectedUserIds.contains(Profile.Columns.id))
.fetchAll(db)
@ -464,7 +464,7 @@ final class EditClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegat
}
ModalActivityIndicatorViewController.present(fromViewController: navigationController) { _ in
Storage.shared
Dependencies()[singleton: .storage]
.writePublisher { db in
// If the user is no longer a member then leave the group
guard !updatedMemberIds.contains(userPublicKey) else { return }

View File

@ -86,7 +86,7 @@ extension ConversationSearchController: UISearchResultsUpdating {
let threadId: String = self.threadId
DispatchQueue.global(qos: .default).async { [weak self] in
let results: [Interaction.TimestampInfo]? = Storage.shared.read { db -> [Interaction.TimestampInfo] in
let results: [Interaction.TimestampInfo]? = Dependencies()[singleton: .storage].read { db -> [Interaction.TimestampInfo] in
self?.resultsBar.willStartSearching(readConnection: db)
return try Interaction.idsForTermWithin(

View File

@ -77,7 +77,7 @@ extension ConversationVC:
@objc func startCall(_ sender: Any?) {
guard SessionCall.isEnabled else { return }
guard viewModel.threadData.threadIsBlocked == false else { return }
guard Storage.shared[.areCallsEnabled] else {
guard Dependencies()[singleton: .storage][.areCallsEnabled] else {
let confirmationModal: ConfirmationModal = ConfirmationModal(
info: ConfirmationModal.Info(
title: "modal_call_permission_request_title".localized(),
@ -111,7 +111,7 @@ extension ConversationVC:
guard AVAudioSession.sharedInstance().recordPermission == .granted else { return }
guard self.viewModel.threadData.threadVariant == .contact else { return }
guard AppEnvironment.shared.callManager.currentCall == nil else { return }
guard let call: SessionCall = Storage.shared.read({ db in SessionCall(db, for: threadId, uuid: UUID().uuidString.lowercased(), mode: .offer, outgoing: true) }) else {
guard let call: SessionCall = Dependencies()[singleton: .storage].read({ db in SessionCall(db, for: threadId, uuid: UUID().uuidString.lowercased(), mode: .offer, outgoing: true) }) else {
return
}
@ -240,14 +240,14 @@ extension ConversationVC:
// MARK: - ExpandingAttachmentsButtonDelegate
func handleGIFButtonTapped() {
guard Storage.shared[.isGiphyEnabled] else {
guard Dependencies()[singleton: .storage][.isGiphyEnabled] else {
let modal: ConfirmationModal = ConfirmationModal(
info: ConfirmationModal.Info(
title: "GIPHY_PERMISSION_TITLE".localized(),
body: .text("GIPHY_PERMISSION_MESSAGE".localized()),
confirmTitle: "continue_2".localized()
) { [weak self] _ in
Storage.shared.writeAsync(
Dependencies()[singleton: .storage].writeAsync(
updates: { db in
db[.isGiphyEnabled] = true
},
@ -539,7 +539,7 @@ extension ConversationVC:
let quoteThumbnailAttachment: Attachment? = optimisticData.quoteModel?.attachment?.cloneAsQuoteThumbnail()
// Actually send the message
dependencies.storage
dependencies[singleton: .storage]
.writePublisher { [weak self] db in
// Update the thread to be visible (if it isn't already)
if self?.viewModel.threadData.threadShouldBeVisible == false {
@ -592,12 +592,13 @@ extension ConversationVC:
)
// Trigger disappear after read
dependencies.jobRunner.upsert(
dependencies[singleton: .jobRunner].upsert(
db,
job: DisappearingMessagesJob.updateNextRunIfNeeded(
db,
interaction: insertedInteraction,
startedAtMs: TimeInterval(SnodeAPI.currentOffsetTimestampMs())
startedAtMs: TimeInterval(SnodeAPI.currentOffsetTimestampMs(using: dependencies)),
using: dependencies
),
canStartJob: true,
using: dependencies
@ -619,14 +620,14 @@ extension ConversationVC:
}
func handleMessageSent() {
if Storage.shared[.playNotificationSoundInForeground] {
if Dependencies()[singleton: .storage][.playNotificationSoundInForeground] {
let soundID = Preferences.Sound.systemSoundId(for: .messageSent, quiet: true)
AudioServicesPlaySystemSound(soundID)
}
let threadId: String = self.viewModel.threadData.threadId
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
TypingIndicators.didStopTyping(db, threadId: threadId, direction: .outgoing)
_ = try SessionThread
@ -642,7 +643,7 @@ extension ConversationVC:
body: .text("modal_link_previews_explanation".localized()),
confirmTitle: "modal_link_previews_button_title".localized()
) { [weak self] _ in
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
db[.areLinkPreviewsEnabled] = true
}
@ -675,7 +676,7 @@ extension ConversationVC:
)
if needsToStartTypingIndicator {
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
TypingIndicators.start(db, threadId: threadId, direction: .outgoing)
}
}
@ -939,8 +940,8 @@ extension ConversationVC:
let threadId: String = self.viewModel.threadData.threadId
// Retry downloading the failed attachment
dependencies.storage.writeAsync { db in
dependencies.jobRunner.add(
dependencies[singleton: .storage].writeAsync { db in
dependencies[singleton: .jobRunner].add(
db,
job: Job(
variant: .attachmentDownload,
@ -1026,7 +1027,7 @@ extension ConversationVC:
case .textOnlyMessage:
if let quote: Quote = cellViewModel.quote {
// Scroll to the original quoted message
let maybeOriginalInteractionInfo: Interaction.TimestampInfo? = Storage.shared.read { db in
let maybeOriginalInteractionInfo: Interaction.TimestampInfo? = Dependencies()[singleton: .storage].read { db in
try quote.originalInteraction
.select(.id, .timestampMs)
.asRequest(of: Interaction.TimestampInfo.self)
@ -1099,7 +1100,7 @@ extension ConversationVC:
// FIXME: Add in support for starting a thread with a 'blinded25' id
guard SessionId.Prefix(from: sessionId) != .blinded25 else { return }
guard SessionId.Prefix(from: sessionId) == .blinded15 else {
Storage.shared.write { db in
Dependencies()[singleton: .storage].write { db in
try SessionThread
.fetchOrCreate(db, id: sessionId, variant: .contact, shouldBeVisible: nil)
}
@ -1116,7 +1117,7 @@ extension ConversationVC:
return
}
let targetThreadId: String? = Storage.shared.write { db in
let targetThreadId: String? = Dependencies()[singleton: .storage].write { db in
let lookup: BlindedIdLookup = try BlindedIdLookup
.fetchOrCreate(
db,
@ -1211,7 +1212,7 @@ extension ConversationVC:
func removeAllReactions(_ cellViewModel: MessageViewModel, for emoji: String, using dependencies: Dependencies) {
guard cellViewModel.threadVariant == .community else { return }
Storage.shared
Dependencies()[singleton: .storage]
.readPublisher { db -> (HTTP.PreparedRequest<OpenGroupAPI.ReactionRemoveAllResponse>, OpenGroupAPI.PendingChange) in
guard
let openGroup: OpenGroup = try? OpenGroup
@ -1258,7 +1259,7 @@ extension ConversationVC:
}
.sinkUntilComplete(
receiveCompletion: { _ in
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
_ = try Reaction
.filter(Reaction.Columns.interactionId == cellViewModel.id)
.filter(Reaction.Columns.emoji == emoji)
@ -1285,7 +1286,7 @@ extension ConversationVC:
let threadVariant: SessionThread.Variant = self.viewModel.threadData.threadVariant
let openGroupRoom: String? = self.viewModel.threadData.openGroupRoomToken
let sentTimestamp: Int64 = SnodeAPI.currentOffsetTimestampMs()
let recentReactionTimestamps: [Int64] = dependencies.caches[.general].recentReactionTimestamps
let recentReactionTimestamps: [Int64] = dependencies[cache: .general].recentReactionTimestamps
guard
recentReactionTimestamps.count < 20 ||
@ -1303,7 +1304,7 @@ extension ConversationVC:
return
}
dependencies.caches.mutate(cache: .general) {
dependencies.mutate(cache: .general) {
$0.recentReactionTimestamps = Array($0.recentReactionTimestamps
.suffix(19))
.appending(sentTimestamp)
@ -1340,7 +1341,7 @@ extension ConversationVC:
}
.subscribe(on: DispatchQueue.global(qos: .userInitiated), using: dependencies)
.flatMap { pendingChange -> AnyPublisher<(MessageSender.PreparedSendData?, OpenGroupInfo?), Error> in
dependencies.storage.writePublisher { [weak self] db -> (MessageSender.PreparedSendData?, OpenGroupInfo?) in
dependencies[singleton: .storage].writePublisher { [weak self] db -> (MessageSender.PreparedSendData?, OpenGroupInfo?) in
// Update the thread to be visible (if it isn't already)
if self?.viewModel.threadData.threadShouldBeVisible == false {
_ = try SessionThread
@ -1496,7 +1497,7 @@ extension ConversationVC:
func handleReactionSentFailure(_ pendingReaction: Reaction?, remove: Bool) {
guard let pendingReaction = pendingReaction else { return }
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
// Reverse the database
if remove {
try pendingReaction.insert(db)
@ -1547,7 +1548,7 @@ extension ConversationVC:
if cellViewModel.state != .failedToSync {
sheet.addAction(UIAlertAction(title: "TXT_DELETE_TITLE".localized(), style: .destructive, handler: { _ in
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try Interaction
.filter(id: cellViewModel.id)
.deleteAll(db)
@ -1614,7 +1615,7 @@ extension ConversationVC:
return presentingViewController.present(errorModal, animated: true, completion: nil)
}
Storage.shared
Dependencies()[singleton: .storage]
.writePublisher { db in
OpenGroupManager.shared.add(
db,
@ -1643,7 +1644,7 @@ extension ConversationVC:
// If there was a failure then the group will be in invalid state until
// the next launch so remove it (the user will be left on the previous
// screen so can re-trigger the join)
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
OpenGroupManager.shared.delete(
db,
openGroupId: OpenGroup.idFor(roomToken: room, server: server),
@ -1710,7 +1711,7 @@ extension ConversationVC:
return
}
dependencies.storage.writeAsync { [weak self] db in
dependencies[singleton: .storage].writeAsync { [weak self] db in
guard
let threadId: String = self?.viewModel.threadData.threadId,
let threadVariant: SessionThread.Variant = self?.viewModel.threadData.threadVariant,
@ -1828,7 +1829,7 @@ extension ConversationVC:
// Info messages and unsent messages should just trigger a local
// deletion (they are created as side effects so we wouldn't be
// able to delete them for all participants anyway)
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
_ = try Interaction
.filter(id: cellViewModel.id)
.deleteAll(db)
@ -1862,7 +1863,7 @@ extension ConversationVC:
case .failure: break
case .finished:
// Delete the interaction (and associated data) from the database
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
_ = try Interaction
.filter(id: cellViewModel.id)
.deleteAll(db)
@ -1884,7 +1885,7 @@ extension ConversationVC:
// Handle open group messages the old way
case .community:
// If it's an incoming message the user must have moderator status
let result: (openGroupServerMessageId: Int64?, openGroup: OpenGroup?)? = Storage.shared.read { db -> (Int64?, OpenGroup?) in
let result: (openGroupServerMessageId: Int64?, openGroup: OpenGroup?)? = dependencies[singleton: .storage].read { db -> (Int64?, OpenGroup?) in
(
try Interaction
.select(.openGroupServerMessageId)
@ -1910,7 +1911,7 @@ extension ConversationVC:
guard cellViewModel.state == .sending || cellViewModel.state == .failed else { return }
// Retrieve any message send jobs for this interaction
let jobs: [Job] = Storage.shared
let jobs: [Job] = dependencies[singleton: .storage]
.read { db in
try? Job
.filter(Job.Columns.variant == Job.Variant.messageSend)
@ -1921,10 +1922,12 @@ extension ConversationVC:
// If the job is currently running then wait until it's done before triggering
// the deletion
let targetJob: Job? = jobs.first(where: { JobRunner.isCurrentlyRunning($0) })
let targetJob: Job? = jobs.first(where: { job -> Bool in
dependencies[singleton: .jobRunner].isCurrentlyRunning(job)
})
guard targetJob == nil else {
JobRunner.afterCurrentlyRunningJob(targetJob) { [weak self] result in
dependencies[singleton: .jobRunner].afterCurrentlyRunningJob(targetJob) { [weak self] result in
switch result {
// If it succeeded then we'll need to delete from the server so re-run
// this function (if we still don't have the server id for some reason
@ -1934,9 +1937,9 @@ extension ConversationVC:
// Otherwise we just need to cancel the pending job (in case it retries)
// and delete the interaction
default:
JobRunner.removePendingJob(targetJob)
dependencies[singleton: .jobRunner].removePendingJob(targetJob)
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
_ = try Interaction
.filter(id: cellViewModel.id)
.deleteAll(db)
@ -1948,9 +1951,9 @@ extension ConversationVC:
// If it's not currently running then remove any pending jobs (just to be safe) and
// delete the interaction locally
jobs.forEach { JobRunner.removePendingJob($0) }
jobs.forEach { dependencies[singleton: .jobRunner].removePendingJob($0) }
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
_ = try Interaction
.filter(id: cellViewModel.id)
.deleteAll(db)
@ -1961,7 +1964,7 @@ extension ConversationVC:
// Delete the message from the open group
deleteRemotely(
from: self,
request: Storage.shared
request: dependencies[singleton: .storage]
.readPublisher { db in
try OpenGroupAPI.preparedMessageDelete(
db,
@ -1982,7 +1985,7 @@ extension ConversationVC:
userPublicKey :
cellViewModel.threadId
)
let serverHash: String? = Storage.shared.read { db -> String? in
let serverHash: String? = dependencies[singleton: .storage].read { db -> String? in
try Interaction
.select(.serverHash)
.filter(id: cellViewModel.id)
@ -1999,7 +2002,7 @@ extension ConversationVC:
// For incoming interactions or interactions with no serverHash just delete them locally
guard cellViewModel.variant == .standardOutgoing, let serverHash: String = serverHash else {
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
_ = try Interaction
.filter(id: cellViewModel.id)
.deleteAll(db)
@ -2027,7 +2030,7 @@ extension ConversationVC:
accessibilityIdentifier: "Delete for me",
style: .destructive
) { [weak self] _ in
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
_ = try Interaction
.filter(id: cellViewModel.id)
.deleteAll(db)
@ -2060,7 +2063,7 @@ extension ConversationVC:
style: .destructive
) { [weak self] _ in
let completeServerDeletion = { [weak self] in
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try MessageSender
.send(
db,
@ -2159,7 +2162,7 @@ extension ConversationVC:
confirmTitle: "BUTTON_OK".localized(),
cancelStyle: .alert_text,
onConfirm: { [weak self] _ in
Storage.shared
Dependencies()[singleton: .storage]
.readPublisher { db -> HTTP.PreparedRequest<NoResponse> in
guard let openGroup: OpenGroup = try OpenGroup.fetchOne(db, id: threadId) else {
throw StorageError.objectNotFound
@ -2215,7 +2218,7 @@ extension ConversationVC:
confirmTitle: "BUTTON_OK".localized(),
cancelStyle: .alert_text,
onConfirm: { [weak self] _ in
dependencies.storage
dependencies[singleton: .storage]
.readPublisher { db in
guard let openGroup: OpenGroup = try OpenGroup.fetchOne(db, id: threadId) else {
throw StorageError.objectNotFound
@ -2407,7 +2410,7 @@ extension ConversationVC:
let threadId: String = self.viewModel.threadData.threadId
let threadVariant: SessionThread.Variant = self.viewModel.threadData.threadVariant
dependencies.storage.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try MessageSender.send(
db,
message: DataExtractionNotification(
@ -2477,11 +2480,11 @@ extension ConversationVC {
// (it'll be updated with correct profile info if they accept the message request so this
// shouldn't cause weird behaviours)
guard
let contact: Contact = Storage.shared.read({ db in Contact.fetchOrCreate(db, id: threadId) }),
let contact: Contact = Dependencies()[singleton: .storage].read({ db in Contact.fetchOrCreate(db, id: threadId) }),
!contact.isApproved
else { return }
Storage.shared
Dependencies()[singleton: .storage]
.writePublisher { db in
// If we aren't creating a new thread (ie. sending a message request) then send a
// messageRequestResponse back to the sender (this allows the sender to know that

View File

@ -407,7 +407,7 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
init(threadId: String, threadVariant: SessionThread.Variant, focusedInteractionInfo: Interaction.TimestampInfo? = nil) {
self.viewModel = ConversationViewModel(threadId: threadId, threadVariant: threadVariant, focusedInteractionInfo: focusedInteractionInfo)
Storage.shared.addObserver(viewModel.pagedDataObserver)
Dependencies()[singleton: .storage].addObserver(viewModel.pagedDataObserver)
super.init(nibName: nil, bundle: nil)
}
@ -617,7 +617,7 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
visibleOnly: true
)
{
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
_ = try SessionThread // Intentionally use `deleteAll` here instead of `deleteOrLeave`
.filter(id: threadId)
.deleteAll(db)
@ -652,7 +652,7 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
private func startObservingChanges(didReturnFromBackground: Bool = false) {
guard dataChangeObservable == nil else { return }
dataChangeObservable = Storage.shared.start(
dataChangeObservable = Dependencies()[singleton: .storage].start(
viewModel.observableThreadData,
onError: { _ in },
onChange: { [weak self] maybeThreadData in
@ -665,7 +665,7 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
SessionId.Prefix(from: sessionId) == .blinded15 ||
SessionId.Prefix(from: sessionId) == .blinded25
),
let blindedLookup: BlindedIdLookup = Storage.shared.read({ db in
let blindedLookup: BlindedIdLookup = Dependencies()[singleton: .storage].read({ db in
try BlindedIdLookup
.filter(id: sessionId)
.fetchOne(db)
@ -689,13 +689,13 @@ final class ConversationVC: BaseVC, SessionUtilRespondingViewController, Convers
// Stop observing changes
self?.stopObservingChanges()
Storage.shared.removeObserver(self?.viewModel.pagedDataObserver)
Dependencies()[singleton: .storage].removeObserver(self?.viewModel.pagedDataObserver)
// Swap the observing to the updated thread
self?.viewModel.swapToThread(updatedThreadId: unblindedId)
// Start observing changes again
Storage.shared.addObserver(self?.viewModel.pagedDataObserver)
Dependencies()[singleton: .storage].addObserver(self?.viewModel.pagedDataObserver)
self?.startObservingChanges()
return
}

View File

@ -47,6 +47,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
private let initialUnreadInteractionId: Int64?
private let markAsReadTrigger: PassthroughSubject<(SessionThreadViewModel.ReadTarget, Int64?), Never> = PassthroughSubject()
private var markAsReadPublisher: AnyPublisher<Void, Never>?
private let dependencies: Dependencies
public lazy var blockedBannerMessage: String = {
switch self.threadData.threadVariant {
@ -64,7 +65,12 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
// MARK: - Initialization
init(threadId: String, threadVariant: SessionThread.Variant, focusedInteractionInfo: Interaction.TimestampInfo?) {
init(
threadId: String,
threadVariant: SessionThread.Variant,
focusedInteractionInfo: Interaction.TimestampInfo?,
using dependencies: Dependencies = Dependencies()
) {
typealias InitialData = (
currentUserPublicKey: String,
initialUnreadInteractionInfo: Interaction.TimestampInfo?,
@ -75,10 +81,10 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
blinded25Key: String?
)
let initialData: InitialData? = Storage.shared.read { db -> InitialData in
let initialData: InitialData? = dependencies[singleton: .storage].read { db -> InitialData in
let interaction: TypedTableAlias<Interaction> = TypedTableAlias()
let groupMember: TypedTableAlias<GroupMember> = TypedTableAlias()
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db)
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
// If we have a specified 'focusedInteractionInfo' then use that, otherwise retrieve the oldest
// unread interaction and start focused around that one
@ -152,6 +158,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
currentUserBlinded25PublicKeyForThisThread: initialData?.blinded25Key
)
self.pagedDataObserver = nil
self.dependencies = dependencies
// Note: Since this references self we need to finish initializing before setting it, we
// also want to skip the initial query and trigger it async so that the push animation
@ -161,7 +168,8 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
for: threadId,
userPublicKey: (initialData?.currentUserPublicKey ?? getUserHexEncodedPublicKey()),
blinded15PublicKey: initialData?.blinded15Key,
blinded25PublicKey: initialData?.blinded25Key
blinded25PublicKey: initialData?.blinded25Key,
using: dependencies
)
// Run the initial query on a background thread so we don't block the push transition
@ -252,7 +260,8 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
for threadId: String,
userPublicKey: String,
blinded15PublicKey: String?,
blinded25PublicKey: String?
blinded25PublicKey: String?,
using dependencies: Dependencies
) -> PagedDatabaseObserver<Interaction, MessageViewModel> {
return PagedDatabaseObserver(
pagedTable: Interaction.self,
@ -365,7 +374,8 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
)
}
)
}
},
using: dependencies
)
}
@ -633,12 +643,15 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
// MARK: - Mentions
public func mentions(for query: String = "") -> [MentionInfo] {
public func mentions(
for query: String = "",
using dependencies: Dependencies = Dependencies()
) -> [MentionInfo] {
let threadData: SessionThreadViewModel = self.threadData
return Storage.shared
return dependencies[singleton: .storage]
.read { db -> [MentionInfo] in
let userPublicKey: String = getUserHexEncodedPublicKey(db)
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
let pattern: FTS5Pattern? = try? SessionThreadViewModel.pattern(db, searchTerm: query, forTable: Profile.self)
let capabilities: Set<Capability.Variant> = (threadData.threadVariant != .community ?
nil :
@ -670,9 +683,12 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
// MARK: - Functions
public func updateDraft(to draft: String) {
public func updateDraft(
to draft: String,
using dependencies: Dependencies = Dependencies()
) {
let threadId: String = self.threadId
let currentDraft: String = Storage.shared
let currentDraft: String = dependencies[singleton: .storage]
.read { db in
try SessionThread
.select(.messageDraft)
@ -685,7 +701,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
// Only write the updated draft to the database if it's changed (avoid unnecessary writes)
guard draft != currentDraft else { return }
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try SessionThread
.filter(id: threadId)
.updateAll(db, SessionThread.Columns.messageDraft.set(to: draft))
@ -741,7 +757,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
markAsReadTrigger.send((target, timestampMs))
}
public func swapToThread(updatedThreadId: String) {
public func swapToThread(updatedThreadId: String, using dependencies: Dependencies = Dependencies()) {
let oldestMessageId: Int64? = self.interactionData
.filter { $0.model == .messages }
.first?
@ -755,7 +771,8 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
for: updatedThreadId,
userPublicKey: getUserHexEncodedPublicKey(),
blinded15PublicKey: nil,
blinded25PublicKey: nil
blinded25PublicKey: nil,
using: dependencies
)
// Try load everything up to the initial visible message, fallback to just the initial page of messages
@ -766,12 +783,12 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
}
}
public func trustContact() {
public func trustContact(using dependencies: Dependencies = Dependencies()) {
guard self.threadData.threadVariant == .contact else { return }
let threadId: String = self.threadId
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try Contact
.filter(id: threadId)
.updateAll(db, Contact.Columns.isTrusted.set(to: true))
@ -782,7 +799,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
.stateInfo(authorId: threadId, state: .pendingDownload)
.fetchAll(db)
.forEach { attachmentDownloadInfo in
JobRunner.add(
dependencies[singleton: .jobRunner].add(
db,
job: Job(
variant: .attachmentDownload,
@ -791,18 +808,20 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
details: AttachmentDownloadJob.Details(
attachmentId: attachmentDownloadInfo.attachmentId
)
)
),
canStartJob: true,
using: dependencies
)
}
}
}
public func unblockContact() {
public func unblockContact(using dependencies: Dependencies = Dependencies()) {
guard self.threadData.threadVariant == .contact else { return }
let threadId: String = self.threadId
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try Contact
.filter(id: threadId)
.updateAllAndConfig(db, Contact.Columns.isBlocked.set(to: false))
@ -1025,7 +1044,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
.firstIndex(where: { $0.id == interactionId }),
currentIndex < (messageSection.elements.count - 1),
messageSection.elements[currentIndex + 1].cellType == .audio,
Storage.shared[.shouldAutoPlayConsecutiveAudioMessages] == true
dependencies[singleton: .storage][.shouldAutoPlayConsecutiveAudioMessages] == true
else { return }
let nextItem: MessageViewModel = messageSection.elements[currentIndex + 1]

View File

@ -75,7 +75,7 @@ class EmojiPickerCollectionView: UICollectionView {
tapGestureRecognizer.delegate = self
// Fetch the emoji data from the database
let maybeEmojiData: (recent: [EmojiWithSkinTones], allGrouped: [Emoji.Category: [EmojiWithSkinTones]])? = Storage.shared.read { db in
let maybeEmojiData: (recent: [EmojiWithSkinTones], allGrouped: [Emoji.Category: [EmojiWithSkinTones]])? = Dependencies()[singleton: .storage].read { db in
// Some emoji have two different code points but identical appearances. Let's remove them!
// If we normalize to a different emoji than the one currently in our array, we want to drop
// the non-normalized variant if the normalized variant already exists. Otherwise, map to the
@ -188,7 +188,7 @@ class EmojiPickerCollectionView: UICollectionView {
currentSkinTonePicker?.dismiss()
currentSkinTonePicker = EmojiSkinTonePicker.present(referenceView: cell, emoji: emoji) { [weak self] emoji in
if let emoji: EmojiWithSkinTones = emoji {
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
emoji.baseEmoji?.setPreferredSkinTones(
db,
preferredSkinTonePermutation: emoji.skinTones

View File

@ -282,14 +282,16 @@ final class InputView: UIView, InputViewButtonDelegate, InputTextViewDelegate, M
quoteView.pin(.bottom, to: .bottom, of: additionalContentContainer, withInset: -6)
}
private func autoGenerateLinkPreviewIfPossible() {
private func autoGenerateLinkPreviewIfPossible(
using dependencies: Dependencies = Dependencies()
) {
// Don't allow link previews on 'none' or 'textOnly' input
guard enabledMessageTypes == .all else { return }
// Suggest that the user enable link previews if they haven't already and we haven't
// told them about link previews yet
let text = inputTextView.text!
let areLinkPreviewsEnabled: Bool = Storage.shared[.areLinkPreviewsEnabled]
let areLinkPreviewsEnabled: Bool = dependencies[singleton: .storage][.areLinkPreviewsEnabled]
if
!LinkPreview.allPreviewUrls(forMessageBodyText: text).isEmpty &&

View File

@ -141,7 +141,7 @@ final class CallMessageCell: MessageCell {
let shouldShowInfoIcon: Bool = (
messageInfo.state == .permissionDenied &&
!Storage.shared[.areCallsEnabled]
!Dependencies()[singleton: .storage][.areCallsEnabled]
)
infoImageViewWidthConstraint.constant = (shouldShowInfoIcon ? CallMessageCell.iconSize : 0)
infoImageViewHeightConstraint.constant = (shouldShowInfoIcon ? CallMessageCell.iconSize : 0)
@ -177,7 +177,7 @@ final class CallMessageCell: MessageCell {
else { return }
// Should only be tappable if the info icon is visible
guard messageInfo.state == .permissionDenied && !Storage.shared[.areCallsEnabled] else { return }
guard messageInfo.state == .permissionDenied && !Dependencies()[singleton: .storage][.areCallsEnabled] else { return }
self.delegate?.handleItemTapped(cellViewModel, gestureRecognizer: gestureRecognizer)
}

View File

@ -483,7 +483,7 @@ class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel<ThreadD
guard self.config != updatedConfig else { return }
dependencies.storage.writeAsync(using: dependencies) { db in
dependencies[singleton: .storage].writeAsync(using: dependencies) { db in
_ = try updatedConfig.saved(db)
_ = try Interaction
@ -523,7 +523,7 @@ class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel<ThreadD
}
// Contacts & legacy closed groups need to update the SessionUtil
dependencies.storage.writeAsync(using: dependencies) { db in
dependencies[singleton: .storage].writeAsync(using: dependencies) { db in
switch threadVariant {
case .contact:
try SessionUtil

View File

@ -72,7 +72,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
self.didTriggerSearch = didTriggerSearch
self.oldDisplayName = (threadVariant != .contact ?
nil :
dependencies.storage.read { db in
dependencies[singleton: .storage].read { db in
try Profile
.filter(id: threadId)
.select(.nickname)
@ -151,7 +151,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
.trimmingCharacters(in: .whitespacesAndNewlines)
self?.oldDisplayName = (updatedNickname.isEmpty ? nil : editedDisplayName)
dependencies.storage.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try Profile
.filter(id: threadId)
.updateAllAndConfig(
@ -532,7 +532,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
cancelStyle: .alert_text
),
onTap: {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try SessionThread.deleteOrLeave(
db,
threadId: threadId,
@ -596,7 +596,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
onTap: {
let newValue: Bool = !(threadViewModel.threadOnlyNotifyForMentions == true)
dependencies.storage.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try SessionThread
.filter(id: threadId)
.updateAll(
@ -635,7 +635,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
label: "Mute notifications"
),
onTap: {
dependencies.storage.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
let currentValue: TimeInterval? = try SessionThread
.filter(id: threadId)
.select(.mutedUntilTimestamp)
@ -719,7 +719,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
}
.removeDuplicates()
.handleEvents(didFail: { SNLog("[ThreadSettingsViewModel] Observation failed with error: \($0)") })
.publisher(in: dependencies.storage, scheduling: dependencies.scheduler)
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
.mapToSessionTableViewData(for: self)
// MARK: - Functions
@ -764,7 +764,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
publicKey: publicKey
)
dependencies.storage.writeAsync { [dependencies] db in
dependencies[singleton: .storage].writeAsync { [dependencies] db in
try selectedUsers.forEach { userId in
let thread: SessionThread = try SessionThread
.fetchOrCreate(db, id: userId, variant: .contact, shouldBeVisible: nil)
@ -810,7 +810,7 @@ class ThreadSettingsViewModel: SessionTableViewModel<ThreadSettingsViewModel.Nav
) {
guard oldBlockedState != isBlocked else { return }
dependencies.storage.writeAsync(
dependencies[singleton: .storage].writeAsync(
updates: { db in
try Contact
.filter(id: threadId)

View File

@ -32,7 +32,7 @@ class GlobalSearchViewController: BaseVC, SessionUtilRespondingViewController, U
// MARK: - Variables
private lazy var defaultSearchResults: [SectionModel] = {
let result: SessionThreadViewModel? = Storage.shared.read { db -> SessionThreadViewModel? in
let result: SessionThreadViewModel? = Dependencies()[singleton: .storage].read { db -> SessionThreadViewModel? in
try SessionThreadViewModel
.noteToSelfOnlyQuery(userPublicKey: getUserHexEncodedPublicKey(db))
.fetchOne(db)
@ -160,7 +160,11 @@ class GlobalSearchViewController: BaseVC, SessionUtilRespondingViewController, U
}
}
private func updateSearchResults(searchText rawSearchText: String, force: Bool = false) {
private func updateSearchResults(
searchText rawSearchText: String,
force: Bool = false,
using dependencies: Dependencies = Dependencies()
) {
let searchText = rawSearchText.stripped
guard searchText.count > 0 else {
@ -178,7 +182,7 @@ class GlobalSearchViewController: BaseVC, SessionUtilRespondingViewController, U
DispatchQueue.global(qos: .default).async { [weak self] in
self?.readConnection.wrappedValue?.interrupt()
let result: Result<[SectionModel], Error>? = Storage.shared.read { db -> Result<[SectionModel], Error> in
let result: Result<[SectionModel], Error>? = dependencies[singleton: .storage].read { db -> Result<[SectionModel], Error> in
self?.readConnection.mutate { $0 = db }
do {
@ -308,7 +312,13 @@ extension GlobalSearchViewController {
}
}
private func show(threadId: String, threadVariant: SessionThread.Variant, focusedInteractionInfo: Interaction.TimestampInfo? = nil, animated: Bool = true) {
private func show(
threadId: String,
threadVariant: SessionThread.Variant,
focusedInteractionInfo: Interaction.TimestampInfo? = nil,
animated: Bool = true,
using dependencies: Dependencies = Dependencies()
) {
guard Thread.isMainThread else {
DispatchQueue.main.async { [weak self] in
self?.show(threadId: threadId, threadVariant: threadVariant, focusedInteractionInfo: focusedInteractionInfo, animated: animated)
@ -319,7 +329,7 @@ extension GlobalSearchViewController {
// If it's a one-to-one thread then make sure the thread exists before pushing to it (in case the
// contact has been hidden)
if threadVariant == .contact {
Storage.shared.write { db in
dependencies[singleton: .storage].write { db in
try SessionThread.fetchOrCreate(
db,
id: threadId,

View File

@ -29,7 +29,7 @@ final class HomeVC: BaseVC, SessionUtilRespondingViewController, UITableViewData
// MARK: - Intialization
init() {
Storage.shared.addObserver(viewModel.pagedDataObserver)
Dependencies()[singleton: .storage].addObserver(viewModel.pagedDataObserver)
super.init(nibName: nil, bundle: nil)
}
@ -282,7 +282,7 @@ final class HomeVC: BaseVC, SessionUtilRespondingViewController, UITableViewData
// Start polling if needed (i.e. if the user just created or restored their Session ID)
if Identity.userExists(), let appDelegate: AppDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.startPollersIfNeeded()
appDelegate.startPollersIfNeeded(using: Dependencies())
}
// Onion request path countries cache
@ -333,7 +333,7 @@ final class HomeVC: BaseVC, SessionUtilRespondingViewController, UITableViewData
runAndClearInitialChangeCallback = nil
}
dataChangeObservable = Storage.shared.start(
dataChangeObservable = Dependencies()[singleton: .storage].start(
viewModel.observableState,
onError: { _ in },
onChange: { [weak self] state in

View File

@ -31,18 +31,20 @@ public class HomeViewModel {
// MARK: - Initialization
init() {
init(
using dependencies: Dependencies = Dependencies()
) {
typealias InitialData = (
showViewedSeedBanner: Bool,
hasHiddenMessageRequests: Bool,
profile: Profile
)
let initialData: InitialData? = Storage.shared.read { db -> InitialData in
let initialData: InitialData? = dependencies[singleton: .storage].read { db -> InitialData in
(
!db[.hasViewedSeed],
db[.hasHiddenMessageRequests],
Profile.fetchOrCreateCurrentUser(db)
Profile.fetchOrCreateCurrentUser(db, using: dependencies)
)
}
@ -217,7 +219,8 @@ public class HomeViewModel {
)
self?.hasReceivedInitialThreadData = true
}
},
using: dependencies
)
// Run the initial query on a background thread so we don't block the main thread

View File

@ -24,7 +24,7 @@ class MessageRequestsViewController: BaseVC, SessionUtilRespondingViewController
// MARK: - Intialization
init() {
Storage.shared.addObserver(viewModel.pagedDataObserver)
Dependencies()[singleton: .storage].addObserver(viewModel.pagedDataObserver)
super.init(nibName: nil, bundle: nil)
}

View File

@ -188,7 +188,7 @@ public class MessageRequestsViewModel {
groupThreadIds: [String]
) {
// Clear the requests
Storage.shared.write { db in
Dependencies()[singleton: .storage].write { db in
// Remove the one-to-one requests
try SessionThread.deleteOrLeave(
db,

View File

@ -31,7 +31,7 @@ public class DocumentTileViewController: UIViewController, UITableViewDelegate,
init(viewModel: MediaGalleryViewModel) {
self.viewModel = viewModel
Storage.shared.addObserver(viewModel.pagedDataObserver)
Dependencies()[singleton: .storage].addObserver(viewModel.pagedDataObserver)
super.init(nibName: nil, bundle: nil)
}

View File

@ -399,7 +399,7 @@ public class MediaGalleryViewModel {
// Note: It's possible we already have cached album data for this interaction
// but to avoid displaying stale data we re-fetch from the database anyway
let maybeAlbumInfo: AlbumInfo? = Storage.shared.read { db -> AlbumInfo in
let maybeAlbumInfo: AlbumInfo? = Dependencies()[singleton: .storage].read { db -> AlbumInfo in
let attachment: TypedTableAlias<Attachment> = TypedTableAlias()
let interaction: TypedTableAlias<Interaction> = TypedTableAlias()
let interactionAttachment: TypedTableAlias<InteractionAttachment> = TypedTableAlias()

View File

@ -393,7 +393,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
guard dataChangeObservable == nil else { return }
// Start observing for data changes
dataChangeObservable = Storage.shared.start(
dataChangeObservable = Dependencies()[singleton: .storage].start(
viewModel.observableAlbumData,
onError: { _ in },
onChange: { [weak self] albumData in
@ -545,7 +545,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
let threadId: String = self.viewModel.threadId
let threadVariant: SessionThread.Variant = self.viewModel.threadVariant
Storage.shared.write { db in
Dependencies()[singleton: .storage].write { db in
try MessageSender.send(
db,
message: DataExtractionNotification(
@ -571,13 +571,13 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
title: "delete_message_for_me".localized(),
style: .destructive
) { _ in
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
_ = try Attachment
.filter(id: itemToDelete.attachment.id)
.deleteAll(db)
// Add the garbage collection job to delete orphaned attachment files
JobRunner.add(
Dependencies()[singleton: .jobRunner].add(
db,
job: Job(
variant: .garbageCollection,
@ -585,7 +585,9 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
details: GarbageCollectionJob.Details(
typesToCollect: [.orphanedAttachmentFiles]
)
)
),
canStartJob: true,
using: Dependencies()
)
// Delete any interactions which had all of their attachments removed
@ -891,7 +893,7 @@ class MediaPageViewController: UIPageViewController, UIPageViewControllerDataSou
let name: String = {
switch targetItem.interactionVariant {
case .standardIncoming:
return Storage.shared
return Dependencies()[singleton: .storage]
.read { db in
Profile.displayName(
db,

View File

@ -39,7 +39,7 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour
init(viewModel: MediaGalleryViewModel) {
self.viewModel = viewModel
Storage.shared.addObserver(viewModel.pagedDataObserver)
Dependencies()[singleton: .storage].addObserver(viewModel.pagedDataObserver)
super.init(nibName: nil, bundle: nil)
}
@ -691,7 +691,7 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour
}()
let deleteAction = UIAlertAction(title: confirmationTitle, style: .destructive) { [weak self] _ in
Storage.shared.writeAsync { db in
Dependencies()[singleton: .storage].writeAsync { db in
let interactionIds: Set<Int64> = items
.map { $0.interactionId }
.asSet()
@ -701,7 +701,7 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour
.deleteAll(db)
// Add the garbage collection job to delete orphaned attachment files
JobRunner.add(
Dependencies()[singleton: .jobRunner].add(
db,
job: Job(
variant: .garbageCollection,
@ -709,7 +709,9 @@ public class MediaTileViewController: UIViewController, UICollectionViewDataSour
details: GarbageCollectionJob.Details(
typesToCollect: [.orphanedAttachmentFiles]
)
)
),
canStartJob: true,
using: Dependencies()
)
// Delete any interactions which had all of their attachments removed

View File

@ -35,6 +35,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
SetCurrentAppContext(MainAppContext())
verifyDBKeysAvailableBeforeBackgroundLaunch()
// Called via the OS so create a default 'Dependencies' instance
let dependencies: Dependencies = Dependencies()
Cryptography.seedRandom()
AppVersion.sharedInstance()
AppEnvironment.shared.pushRegistrationManager.createVoipRegistryIfNecessary()
@ -74,7 +76,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
if case .failure(let error) = result {
DispatchQueue.main.async {
self?.initialLaunchFailed = true
self?.showFailedStartupAlert(calledFrom: .finishLaunching, error: .databaseError(error))
self?.showFailedStartupAlert(
calledFrom: .finishLaunching,
error: .databaseError(error),
using: dependencies
)
}
return
}
@ -84,7 +90,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
/// **Note:** Need to do this after the db migrations because theme preferences are stored in the database and
/// we don't want to access it until after the migrations run
ThemeManager.mainWindow = mainWindow
self?.completePostMigrationSetup(calledFrom: .finishLaunching, needsConfigSync: needsConfigSync)
self?.completePostMigrationSetup(
calledFrom: .finishLaunching,
needsConfigSync: needsConfigSync,
using: dependencies
)
}
)
@ -135,14 +145,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
/// Apple's documentation on the matter)
UNUserNotificationCenter.current().delegate = self
Storage.resumeDatabaseAccess()
// Called via the OS so create a default 'Dependencies' instance
let dependencies: Dependencies = Dependencies()
Storage.resumeDatabaseAccess(using: dependencies)
// Reset the 'startTime' (since it would be invalid from the last launch)
startTime = CACurrentMediaTime()
// If we've already completed migrations at least once this launch then check
// to see if any "delayed" migrations now need to run
if Storage.shared.hasCompletedMigrations {
if dependencies[singleton: .storage].hasCompletedMigrations {
SNLog("Checking for pending migrations")
let initialLaunchFailed: Bool = self.initialLaunchFailed
@ -169,7 +181,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
DispatchQueue.main.async {
self?.showFailedStartupAlert(
calledFrom: .enterForeground(initialLaunchFailed: initialLaunchFailed),
error: .databaseError(error)
error: .databaseError(error),
using: dependencies
)
}
return
@ -177,9 +190,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
self?.completePostMigrationSetup(
calledFrom: .enterForeground(initialLaunchFailed: initialLaunchFailed),
needsConfigSync: needsConfigSync
needsConfigSync: needsConfigSync,
using: dependencies
)
}
},
using: dependencies
)
}
}
@ -190,12 +205,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
DDLog.flushLog()
// Called via the OS so create a default 'Dependencies' instance
let dependencies: Dependencies = Dependencies()
// NOTE: Fix an edge case where user taps on the callkit notification
// but answers the call on another device
stopPollers(shouldStopUserPoller: !self.hasCallOngoing())
stopPollers(shouldStopUserPoller: !self.hasCallOngoing(), using: dependencies)
// Stop all jobs except for message sending and when completed suspend the database
JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend) {
dependencies[singleton: .jobRunner].stopAndClearPendingJobs(exceptForVariant: .messageSend) {
if !self.hasCallOngoing() {
Storage.suspendDatabaseAccess()
}
@ -215,12 +233,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func applicationDidBecomeActive(_ application: UIApplication) {
guard !SNUtilitiesKit.isRunningTests else { return }
// Called via the OS so create a default 'Dependencies' instance
let dependencies: Dependencies = Dependencies()
UserDefaults.sharedLokiProject?[.isMainAppActive] = true
ensureRootViewController(calledFrom: .didBecomeActive)
ensureRootViewController(calledFrom: .didBecomeActive, using: dependencies)
AppReadiness.runNowOrWhenAppDidBecomeReady { [weak self] in
self?.handleActivation()
self?.handleActivation(using: dependencies)
/// Clear all notifications whenever we become active once the app is ready
///
@ -229,7 +249,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
/// within the `runNowOrWhenAppDidBecomeReady` callback and dispatch to the next run loop to ensure it runs after
/// the notification has actually been handled
DispatchQueue.main.async { [weak self] in
self?.clearAllNotificationsAndRestoreBadgeCount()
self?.clearAllNotificationsAndRestoreBadgeCount(using: dependencies)
}
}
@ -312,13 +332,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// MARK: - App Readiness
private func completePostMigrationSetup(calledFrom lifecycleMethod: LifecycleMethod, needsConfigSync: Bool) {
private func completePostMigrationSetup(
calledFrom lifecycleMethod: LifecycleMethod,
needsConfigSync: Bool,
using dependencies: Dependencies
) {
SNLog("Migrations completed, performing setup and ensuring rootViewController")
Configuration.performMainSetup()
JobRunner.setExecutor(SyncPushTokensJob.self, for: .syncPushTokens)
Configuration.performMainSetup(using: dependencies)
dependencies[singleton: .jobRunner].setExecutor(SyncPushTokensJob.self, for: .syncPushTokens)
// Setup the UI if needed, then trigger any post-UI setup actions
self.ensureRootViewController(calledFrom: lifecycleMethod) { [weak self] success in
self.ensureRootViewController(calledFrom: lifecycleMethod, using: dependencies) { [weak self] success in
// If we didn't successfully ensure the rootViewController then don't continue as
// the user is in an invalid state (and should have already been shown a modal)
guard success else { return }
@ -326,12 +350,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
SNLog("RootViewController ready, readying remaining processes")
self?.initialLaunchFailed = false
/// Trigger any launch-specific jobs and start the JobRunner with `JobRunner.appDidFinishLaunching()` some
/// Trigger any launch-specific jobs and start the JobRunner with `jobRunner.appDidFinishLaunching(using:)` some
/// of these jobs (eg. DisappearingMessages job) can impact the interactions which get fetched to display on the home
/// screen, if the PagedDatabaseObserver hasn't been setup yet then the home screen can show stale (ie. deleted)
/// interactions incorrectly
if lifecycleMethod == .finishLaunching {
JobRunner.appDidFinishLaunching()
dependencies[singleton: .jobRunner].appDidFinishLaunching(using: dependencies)
}
/// Flag that the app is ready via `AppReadiness.setAppIsReady()`
@ -341,7 +365,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
///
/// **Note:** This this does much more than set a flag - it will also run all deferred blocks (including the JobRunner
/// `appDidBecomeActive` method hence why it **must** also come after calling
/// `JobRunner.appDidFinishLaunching()`)
/// `jobRunner.appDidFinishLaunching(using:)`)
AppReadiness.setAppIsReady()
/// Remove the sleep blocking once the startup is done (needs to run on the main thread and sleeping while
@ -352,7 +376,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
AppVersion.sharedInstance().mainAppLaunchDidComplete()
/// App won't be ready for extensions and no need to enqueue a config sync unless we successfully completed startup
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
// Increment the launch count (guaranteed to change which results in the write actually
// doing something and outputting and error if the DB is suspended)
db[.activeCounter] = ((db[.activeCounter] ?? 0) + 1)
@ -391,6 +415,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
calledFrom lifecycleMethod: LifecycleMethod,
error: StartupError,
animated: Bool = true,
using dependencies: Dependencies,
presentationCompletion: (() -> ())? = nil
) {
/// This **must** be a standard `UIAlertController` instead of a `ConfirmationModal` because we may not
@ -408,7 +433,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// the app)
guard self?.hasInitialRootViewController == false else { return }
self?.showFailedStartupAlert(calledFrom: lifecycleMethod, error: error)
self?.showFailedStartupAlert(calledFrom: lifecycleMethod, error: error, using: dependencies)
}
})
@ -439,13 +464,22 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
switch result {
case .failure:
DispatchQueue.main.async {
self?.showFailedStartupAlert(calledFrom: lifecycleMethod, error: .failedToRestore)
self?.showFailedStartupAlert(
calledFrom: lifecycleMethod,
error: .failedToRestore,
using: dependencies
)
}
case .success:
self?.completePostMigrationSetup(calledFrom: lifecycleMethod, needsConfigSync: needsConfigSync)
self?.completePostMigrationSetup(
calledFrom: lifecycleMethod,
needsConfigSync: needsConfigSync,
using: dependencies
)
}
}
},
using: dependencies
)
})
@ -498,19 +532,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
}
private func handleActivation() {
private func handleActivation(using dependencies: Dependencies = Dependencies()) {
/// There is a _fun_ behaviour here where if the user launches the app, sends it to the background at the right time and then
/// opens it again the `AppReadiness` closures can be triggered before `applicationDidBecomeActive` has been
/// called again - this can result in odd behaviours so hold off on running this logic until it's properly called again
guard
Identity.userExists() &&
Identity.userExists(using: dependencies) &&
UserDefaults.sharedLokiProject?[.isMainAppActive] == true
else { return }
enableBackgroundRefreshIfNecessary()
JobRunner.appDidBecomeActive()
dependencies[singleton: .jobRunner].appDidBecomeActive(using: dependencies)
startPollersIfNeeded()
startPollersIfNeeded(using: dependencies)
if CurrentAppContext().isMainApp {
handleAppActivatedWithOngoingCallIfNeeded()
@ -519,13 +553,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
private func ensureRootViewController(
calledFrom lifecycleMethod: LifecycleMethod,
using dependencies: Dependencies,
onComplete: @escaping ((Bool) -> ()) = { _ in }
) {
let hasInitialRootViewController: Bool = self.hasInitialRootViewController
// Always call the completion block and indicate whether we successfully created the UI
guard
Storage.shared.isValid &&
dependencies[singleton: .storage].isValid &&
(
AppReadiness.isAppReady() ||
lifecycleMethod == .finishLaunching ||
@ -541,7 +576,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
repeats: false
) { [weak self] timer in
timer.invalidate()
self?.showFailedStartupAlert(calledFrom: lifecycleMethod, error: .startupTimeout)
self?.showFailedStartupAlert(
calledFrom: lifecycleMethod,
error: .startupTimeout,
using: dependencies
)
}
// All logic which needs to run after the 'rootViewController' is created
@ -577,7 +616,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
case is UIAlertController, is ConfirmationModal:
/// If the viewController we were presenting happened to be the "failed startup" modal then we can dismiss it
/// automatically (while this seems redundant it's less jarring for the user than just instantly having it disappear)
self?.showFailedStartupAlert(calledFrom: lifecycleMethod, error: .startupTimeout, animated: false) {
self?.showFailedStartupAlert(
calledFrom: lifecycleMethod,
error: .startupTimeout,
animated: false,
using: dependencies
) {
self?.window?.rootViewController?.dismiss(animated: true)
}
@ -642,7 +686,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
#endif
}
private func clearAllNotificationsAndRestoreBadgeCount() {
private func clearAllNotificationsAndRestoreBadgeCount(using dependencies: Dependencies = Dependencies()) {
AppReadiness.runNowOrWhenAppDidBecomeReady {
AppEnvironment.shared.notificationPresenter.clearAllNotifications()
@ -652,9 +696,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
/// read pools (up to a few seconds), since this read is blocking we want to dispatch it to run async to ensure
/// we don't block user interaction while it's running
DispatchQueue.global(qos: .default).async {
let unreadCount: Int = Storage.shared
let unreadCount: Int = dependencies[singleton: .storage]
.read { db in
let userPublicKey: String = getUserHexEncodedPublicKey(db)
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
let thread: TypedTableAlias<SessionThread> = TypedTableAlias()
return try Interaction
@ -766,28 +810,34 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// MARK: - Polling
public func startPollersIfNeeded(shouldStartGroupPollers: Bool = true) {
guard Identity.userExists() else { return }
public func startPollersIfNeeded(
shouldStartGroupPollers: Bool = true,
using dependencies: Dependencies
) {
guard Identity.userExists(using: dependencies) else { return }
/// There is a fun issue where if you launch without any valid paths then the pollers are guaranteed to fail their first poll due to
/// trying and failing to build paths without having the `SnodeAPI.snodePool` populated, by waiting for the
/// `JobRunner.blockingQueue` to complete we can have more confidence that paths won't fail to build incorrectly
JobRunner.afterBlockingQueue { [weak self] in
/// `jobRunner.blockingQueue` to complete we can have more confidence that paths won't fail to build incorrectly
dependencies[singleton: .jobRunner].afterBlockingQueue { [weak self] in
self?.poller.start()
guard shouldStartGroupPollers else { return }
ClosedGroupPoller.shared.start()
dependencies[singleton: .closedGroupPoller].start(using: dependencies)
OpenGroupManager.shared.startPolling()
}
}
public func stopPollers(shouldStopUserPoller: Bool = true) {
public func stopPollers(
shouldStopUserPoller: Bool = true,
using dependencies: Dependencies = Dependencies()
) {
if shouldStopUserPoller {
poller.stopAllPollers()
}
ClosedGroupPoller.shared.stopAllPollers()
dependencies[singleton: .closedGroupPoller].stopAllPollers()
OpenGroupManager.shared.stopPolling()
}

View File

@ -40,9 +40,10 @@ public struct SessionApp {
variant: SessionThread.Variant,
action: ConversationViewModel.Action = .none,
dismissing presentingViewController: UIViewController?,
animated: Bool
animated: Bool,
using dependencies: Dependencies = Dependencies()
) {
let threadInfo: (threadExists: Bool, isMessageRequest: Bool)? = Storage.shared.read { db in
let threadInfo: (threadExists: Bool, isMessageRequest: Bool)? = dependencies[singleton: .storage].read { db in
let isMessageRequest: Bool = {
switch variant {
case .contact:
@ -50,7 +51,7 @@ public struct SessionApp {
.isMessageRequest(
id: threadId,
variant: .contact,
currentUserPublicKey: getUserHexEncodedPublicKey(db),
currentUserPublicKey: getUserHexEncodedPublicKey(db, using: dependencies),
shouldBeVisible: nil,
contactIsApproved: (try? Contact
.filter(id: threadId)
@ -86,7 +87,7 @@ public struct SessionApp {
/// should do it on a background thread just in case something is keeping the DBWrite thread busy as in the past this could cause the app to hang
guard threadInfo?.threadExists == true else {
DispatchQueue.global(qos: .userInitiated).async {
Storage.shared.write { db in
dependencies[singleton: .storage].write { db in
try SessionThread.fetchOrCreate(db, id: threadId, variant: variant, shouldBeVisible: nil)
}

View File

@ -512,7 +512,7 @@ public class NotificationPresenter: NotificationsProtocol {
private func checkIfShouldPlaySound(applicationState: UIApplication.State) -> Bool {
guard applicationState == .active else { return true }
guard Storage.shared[.playNotificationSoundInForeground] else { return false }
guard Dependencies()[singleton: .storage][.playNotificationSoundInForeground] else { return false }
let nowMs: UInt64 = UInt64(floor(Date().timeIntervalSince1970 * 1000))
let recentThreshold = nowMs - UInt64(kAudioNotificationsThrottleInterval * Double(kSecondInMs))
@ -544,7 +544,7 @@ class NotificationActionHandler {
.eraseToAnyPublisher()
}
guard Storage.shared.read({ db in try SessionThread.exists(db, id: threadId) }) == true else {
guard Dependencies()[singleton: .storage].read({ db in try SessionThread.exists(db, id: threadId) }) == true else {
return Fail(error: NotificationError.failDebug("unable to find thread with id: \(threadId)"))
.eraseToAnyPublisher()
}
@ -563,12 +563,12 @@ class NotificationActionHandler {
.eraseToAnyPublisher()
}
guard let thread: SessionThread = Storage.shared.read({ db in try SessionThread.fetchOne(db, id: threadId) }) else {
guard let thread: SessionThread = dependencies[singleton: .storage].read({ db in try SessionThread.fetchOne(db, id: threadId) }) else {
return Fail<Void, Error>(error: NotificationError.failDebug("unable to find thread with id: \(threadId)"))
.eraseToAnyPublisher()
}
return dependencies.storage
return dependencies[singleton: .storage]
.writePublisher { db in
let interaction: Interaction = try Interaction(
threadId: threadId,
@ -607,7 +607,7 @@ class NotificationActionHandler {
switch result {
case .finished: break
case .failure:
Storage.shared.read { [weak self] db in
dependencies[singleton: .storage].read { [weak self] db in
self?.notificationPresenter.notifyForFailedSend(
db,
in: thread,
@ -651,7 +651,7 @@ class NotificationActionHandler {
threadId: String,
using dependencies: Dependencies = Dependencies()
) -> AnyPublisher<Void, Error> {
return dependencies.storage
return dependencies[singleton: .storage]
.writePublisher { db in
guard
let threadVariant: SessionThread.Variant = try SessionThread

View File

@ -286,11 +286,13 @@ public enum PushRegistrationError: Error {
return
}
// Called via the OS so create a default 'Dependencies' instance
let dependencies: Dependencies = Dependencies()
Storage.resumeDatabaseAccess()
let maybeCall: SessionCall? = Storage.shared.write { db in
let maybeCall: SessionCall? = dependencies[singleton: .storage].write { db in
let messageInfo: CallMessage.MessageInfo = CallMessage.MessageInfo(
state: (caller == getUserHexEncodedPublicKey(db) ?
state: (caller == getUserHexEncodedPublicKey(db, using: dependencies) ?
.outgoing :
.incoming
)
@ -327,7 +329,7 @@ public enum PushRegistrationError: Error {
}
// NOTE: Just start 1-1 poller so that it won't wait for polling group messages
(UIApplication.shared.delegate as? AppDelegate)?.startPollersIfNeeded(shouldStartGroupPollers: false)
(UIApplication.shared.delegate as? AppDelegate)?.startPollersIfNeeded(shouldStartGroupPollers: false, using: dependencies)
call.reportIncomingCallIfNeeded { error in
if let error = error {

View File

@ -37,7 +37,7 @@ public enum SyncPushTokensJob: JobExecutor {
// If the job is running and 'Fast Mode' is disabled then we should try to unregister the existing
// token
guard isUsingFullAPNs else {
Just(dependencies.storage[.lastRecordedPushToken])
Just(dependencies[singleton: .storage][.lastRecordedPushToken])
.setFailureType(to: Error.self)
.flatMap { lastRecordedPushToken -> AnyPublisher<Void, Error> in
// Tell the device to unregister for remote notifications (essentially try to invalidate
@ -46,7 +46,7 @@ public enum SyncPushTokensJob: JobExecutor {
DispatchQueue.main.sync { UIApplication.shared.unregisterForRemoteNotifications() }
// Clear the old token
dependencies.storage.write(using: dependencies) { db in
dependencies[singleton: .storage].write(using: dependencies) { db in
db[.lastRecordedPushToken] = nil
}
@ -102,9 +102,9 @@ public enum SyncPushTokensJob: JobExecutor {
case .finished:
Logger.warn("Recording push tokens locally. pushToken: \(redact(pushToken)), voipToken: \(redact(voipToken))")
SNLog("[SyncPushTokensJob] Completed")
dependencies.standardUserDefaults[.lastPushNotificationSync] = dependencies.dateNow
dependencies[singleton: .standardUserDefaults][.lastPushNotificationSync] = dependencies.dateNow
dependencies.storage.write(using: dependencies) { db in
dependencies[singleton: .storage].write(using: dependencies) { db in
db[.lastRecordedPushToken] = pushToken
db[.lastRecordedVoipToken] = voipToken
}

View File

@ -48,7 +48,7 @@ enum Onboarding {
.map { _ -> String? in
guard requestId == profileNameRetrievalIdentifier.wrappedValue else { return nil }
return Storage.shared.read { db in
return dependencies[singleton: .storage].read { db in
try Profile
.filter(id: userPublicKey)
.select(.name)
@ -89,7 +89,7 @@ enum Onboarding {
SessionUtil.clearMemoryState(using: dependencies)
// Clear any data which gets set during Onboarding
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
db[.hasViewedSeed] = false
try SessionThread.deleteAll(db)
@ -104,7 +104,7 @@ enum Onboarding {
profileNameRetrievalIdentifier.mutate { $0 = nil }
profileNameRetrievalPublisher.mutate { $0 = nil }
dependencies.standardUserDefaults[.hasSyncedInitialConfiguration] = false
dependencies[singleton: .standardUserDefaults][.hasSyncedInitialConfiguration] = false
}
func preregister(
@ -116,7 +116,7 @@ enum Onboarding {
let x25519PublicKey = x25519KeyPair.hexEncodedPublicKey
// Store the user identity information
Storage.shared.write { db in
dependencies[singleton: .storage].write { db in
try Identity.store(
db,
seed: seed,
@ -166,7 +166,7 @@ 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.
dependencies.standardUserDefaults[.hasSyncedInitialConfiguration] = (self == .register)
dependencies[singleton: .standardUserDefaults][.hasSyncedInitialConfiguration] = (self == .register)
// Only continue if this isn't a new account
guard self != .register else { return }
@ -182,7 +182,7 @@ enum Onboarding {
// 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)
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try Profile
.filter(id: getUserHexEncodedPublicKey(db))
.updateAllAndConfig(

View File

@ -155,7 +155,7 @@ final class PNModeVC: BaseVC, OptionViewDelegate {
// Check if we already have a profile name (ie. profile retrieval completed while waiting on
// this screen)
let existingProfileName: String? = Storage.shared
let existingProfileName: String? = Dependencies()[singleton: .storage]
.read { db in
try Profile
.filter(id: getUserHexEncodedPublicKey(db))

View File

@ -6,11 +6,11 @@ import SessionUtilitiesKit
import SignalUtilitiesKit
final class SeedVC: BaseVC {
public static func mnemonic() throws -> String {
let dbIsValid: Bool = Storage.shared.isValid
let dbIsSuspendedUnsafe: Bool = Storage.shared.isSuspendedUnsafe
public static func mnemonic(using dependencies: Dependencies = Dependencies()) throws -> String {
let dbIsValid: Bool = dependencies[singleton: .storage].isValid
let dbIsSuspendedUnsafe: Bool = dependencies[singleton: .storage].isSuspendedUnsafe
if let hexEncodedSeed: String = Identity.fetchHexEncodedSeed() {
if let hexEncodedSeed: String = Identity.fetchHexEncodedSeed(using: dependencies) {
return Mnemonic.encode(hexEncodedString: hexEncodedSeed)
}
@ -131,7 +131,7 @@ final class SeedVC: BaseVC {
// Set up mnemonic label
mnemonicLabel.text = redactedMnemonic
let mnemonicLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
let mnemonicLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonicTapped))
mnemonicLabel.addGestureRecognizer(mnemonicLabelGestureRecognizer)
mnemonicLabel.isUserInteractionEnabled = true
mnemonicLabel.isEnabled = true
@ -151,7 +151,7 @@ final class SeedVC: BaseVC {
callToActionLabel.themeTextColor = .textSecondary
callToActionLabel.textAlignment = .center
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
let callToActionLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonicTapped))
callToActionLabel.addGestureRecognizer(callToActionLabelGestureRecognizer)
callToActionLabel.isUserInteractionEnabled = true
callToActionLabel.isEnabled = true
@ -222,7 +222,9 @@ final class SeedVC: BaseVC {
dismiss(animated: true, completion: nil)
}
@objc private func revealMnemonic() {
@objc private func revealMnemonicTapped() { revealMnemonic() }
private func revealMnemonic(using dependencies: Dependencies = Dependencies()) {
UIView.transition(with: mnemonicLabel, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.mnemonicLabel.text = self.mnemonic
self.mnemonicLabel.themeTextColor = .textPrimary
@ -246,7 +248,7 @@ final class SeedVC: BaseVC {
}, completion: nil)
seedReminderView.setProgress(1, animated: true)
Storage.shared.writeAsync { db in db[.hasViewedSeed] = true }
dependencies[singleton: .storage].writeAsync { db in db[.hasViewedSeed] = true }
}
@objc private func copyMnemonic() {

View File

@ -163,20 +163,28 @@ final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewC
joinOpenGroup(roomToken: room, server: server, publicKey: publicKey, shouldOpenCommunity: true, onError: onError)
}
fileprivate func joinOpenGroup(roomToken: String, server: String, publicKey: String, shouldOpenCommunity: Bool, onError: (() -> ())?) {
fileprivate func joinOpenGroup(
roomToken: String,
server: String,
publicKey: String,
shouldOpenCommunity: Bool,
using dependencies: Dependencies = Dependencies(),
onError: (() -> ())?
) {
guard !isJoining, let navigationController: UINavigationController = navigationController else { return }
isJoining = true
ModalActivityIndicatorViewController.present(fromViewController: navigationController, canCancel: false) { [weak self] _ in
Storage.shared
dependencies[singleton: .storage]
.writePublisher { db in
OpenGroupManager.shared.add(
db,
roomToken: roomToken,
server: server,
publicKey: publicKey,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
.flatMap { successfullyAddedGroup in
@ -185,7 +193,8 @@ final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewC
roomToken: roomToken,
server: server,
publicKey: publicKey,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
@ -197,7 +206,7 @@ final class JoinOpenGroupVC: BaseVC, UIPageViewControllerDataSource, UIPageViewC
// If there was a failure then the group will be in invalid state until
// the next launch so remove it (the user will be left on the previous
// screen so can re-trigger the join)
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
OpenGroupManager.shared.delete(
db,
openGroupId: OpenGroup.idFor(roomToken: roomToken, server: server),

View File

@ -5,6 +5,7 @@ import Reachability
import SessionUIKit
import SessionSnodeKit
import SessionMessagingKit
import SessionUtilitiesKit
final class PathStatusView: UIView {
enum Size {
@ -45,10 +46,15 @@ final class PathStatusView: UIView {
// MARK: - Initialization
private let size: Size
private let dependencies: Dependencies
private let reachability: Reachability? = Environment.shared?.reachabilityManager.reachability
init(size: Size = .small) {
init(
size: Size = .small,
using dependencies: Dependencies = Dependencies()
) {
self.size = size
self.dependencies = dependencies
super.init(frame: .zero)
@ -58,6 +64,7 @@ final class PathStatusView: UIView {
required init?(coder: NSCoder) {
self.size = .small
self.dependencies = Dependencies()
super.init(coder: coder)
@ -77,7 +84,7 @@ final class PathStatusView: UIView {
self.set(.width, to: self.size.pointSize)
self.set(.height, to: self.size.pointSize)
switch (reachability?.isReachable(), OnionRequestAPI.paths.isEmpty) {
switch (reachability?.isReachable(), dependencies[cache: .onionRequestAPI].paths.isEmpty) {
case (.some(false), _), (nil, _): setStatus(to: .error)
case (.some(true), true): setStatus(to: .connecting)
case (.some(true), false): setStatus(to: .connected)
@ -153,6 +160,6 @@ final class PathStatusView: UIView {
return
}
setStatus(to: (!OnionRequestAPI.paths.isEmpty ? .connected : .connecting))
setStatus(to: (!dependencies[cache: .onionRequestAPI].paths.isEmpty ? .connected : .connecting))
}
}

View File

@ -6,6 +6,7 @@ import NVActivityIndicatorView
import SessionMessagingKit
import SessionUIKit
import SessionSnodeKit
import SessionUtilitiesKit
final class PathVC: BaseVC {
public static let dotSize: CGFloat = 8
@ -135,7 +136,7 @@ final class PathVC: BaseVC {
private func update() {
pathStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
guard let pathToDisplay: [Snode] = OnionRequestAPI.paths.first else {
guard let pathToDisplay: [Snode] = Dependencies()[cache: .onionRequestAPI].paths.first else {
spinner.startAnimating()
UIView.animate(withDuration: 0.25) {
@ -338,7 +339,7 @@ private final class LineView: UIView {
}
}
switch (reachability?.isReachable(), OnionRequestAPI.paths.isEmpty) {
switch (reachability?.isReachable(), Dependencies()[cache: .onionRequestAPI].paths.isEmpty) {
case (.some(false), _), (nil, _): setStatus(to: .error)
case (.some(true), true): setStatus(to: .connecting)
case (.some(true), false): setStatus(to: .connected)
@ -420,6 +421,6 @@ private final class LineView: UIView {
return
}
setStatus(to: (!OnionRequestAPI.paths.isEmpty ? .connected : .connecting))
setStatus(to: (!Dependencies()[cache: .onionRequestAPI].paths.isEmpty ? .connected : .connecting))
}
}

View File

@ -173,7 +173,7 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
].flatMap { $0 }
}
private func unblockTapped() {
private func unblockTapped(using dependencies: Dependencies = Dependencies()) {
guard !selectedContactIdsSubject.value.isEmpty else { return }
let contactIds: Set<String> = selectedContactIdsSubject.value
@ -244,7 +244,7 @@ class BlockedContactsViewModel: SessionTableViewModel<NoNav, BlockedContactsView
cancelStyle: .alert_text
) { [weak self] _ in
// Unblock the contacts
Storage.shared.write { db in
dependencies[singleton: .storage].write { db in
_ = try Contact
.filter(ids: contactIds)
.updateAllAndConfig(db, Contact.Columns.isBlocked.set(to: false))

View File

@ -31,6 +31,18 @@ class ConversationSettingsViewModel: SessionTableViewModel<NoNav, ConversationSe
}
}
private let dependencies: Dependencies
// MARK: - Initialization
init(
using dependencies: Dependencies = Dependencies()
) {
self.dependencies = dependencies
super.init()
}
// MARK: - Content
private struct State: Equatable {
@ -58,9 +70,9 @@ class ConversationSettingsViewModel: SessionTableViewModel<NoNav, ConversationSe
}
.removeDuplicates()
.handleEvents(didFail: { SNLog("[ConversationSettingsViewModel] Observation failed with error: \($0)") })
.publisher(in: Storage.shared)
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
.withPrevious()
.map { (previous: State?, current: State) -> [SectionModel] in
.map { [dependencies] (previous: State?, current: State) -> [SectionModel] in
return [
SectionModel(
model: .messageTrimming,
@ -77,7 +89,7 @@ class ConversationSettingsViewModel: SessionTableViewModel<NoNav, ConversationSe
)
),
onTap: {
Storage.shared.write { db in
dependencies[singleton: .storage].write { db in
db[.trimOpenGroupMessagesOlderThanSixMonths] = !db[.trimOpenGroupMessagesOlderThanSixMonths]
}
}
@ -99,7 +111,7 @@ class ConversationSettingsViewModel: SessionTableViewModel<NoNav, ConversationSe
)
),
onTap: {
Storage.shared.write { db in
dependencies[singleton: .storage].write { db in
db[.shouldAutoPlayConsecutiveAudioMessages] = !db[.shouldAutoPlayConsecutiveAudioMessages]
}
}

View File

@ -29,6 +29,16 @@ class HelpViewModel: SessionTableViewModel<NoNav, HelpViewModel.Section, HelpVie
var style: SessionTableSectionStyle { .padding }
}
// MARK: - Initialization
private let dependencies: Dependencies
init(using dependencies: Dependencies = Dependencies()) {
self.dependencies = dependencies
super.init()
}
// MARK: - Content
override var title: String { "HELP_TITLE".localized() }
@ -169,7 +179,7 @@ class HelpViewModel: SessionTableViewModel<NoNav, HelpViewModel.Section, HelpVie
}
.removeDuplicates()
.handleEvents(didFail: { SNLog("[HelpViewModel] Observation failed with error: \($0)") })
.publisher(in: Storage.shared)
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
.mapToSessionTableViewData(for: self)
// MARK: - Functions
@ -242,7 +252,7 @@ class HelpViewModel: SessionTableViewModel<NoNav, HelpViewModel.Section, HelpVie
),
confirmTitle: "Export",
dismissOnConfirm: false,
onConfirm: { [weak self] modal in
onConfirm: { [weak self, dependencies] modal in
modal.dismiss(animated: true) {
guard let password: String = self?.databaseKeyEncryptionPassword, password.count >= 6 else {
self?.transitionToScreen(
@ -258,7 +268,7 @@ class HelpViewModel: SessionTableViewModel<NoNav, HelpViewModel.Section, HelpVie
}
do {
let exportInfo = try Storage.shared.exportInfo(password: password)
let exportInfo = try dependencies[singleton: .storage].exportInfo(password: password)
let shareVC = UIActivityViewController(
activityItems: [
URL(fileURLWithPath: exportInfo.dbPath),

View File

@ -8,17 +8,14 @@ import SessionMessagingKit
import SessionUtilitiesKit
class NotificationContentViewModel: SessionTableViewModel<NoNav, NotificationSettingsViewModel.Section, Preferences.NotificationPreviewType> {
private let storage: Storage
private let scheduler: ValueObservationScheduler
private let dependencies: Dependencies
// MARK: - Initialization
init(
storage: Storage = Storage.shared,
scheduling scheduler: ValueObservationScheduler = Storage.defaultPublisherScheduler
using dependencies: Dependencies = Dependencies()
) {
self.storage = storage
self.scheduler = scheduler
self.dependencies = dependencies
}
// MARK: - Section
@ -41,7 +38,7 @@ class NotificationContentViewModel: SessionTableViewModel<NoNav, NotificationSet
/// fetch (after the ones in `ValueConcurrentObserver.asyncStart`/`ValueConcurrentObserver.syncStart`)
/// just in case the database has changed between the two reads - unfortunately it doesn't look like there is a way to prevent this
private lazy var _observableTableData: ObservableData = ValueObservation
.trackingConstantRegion { [storage] db -> [SectionModel] in
.trackingConstantRegion { [dependencies] db -> [SectionModel] in
let currentSelection: Preferences.NotificationPreviewType? = db[.preferencesNotificationPreviewType]
.defaulting(to: .defaultPreviewType)
@ -57,7 +54,7 @@ class NotificationContentViewModel: SessionTableViewModel<NoNav, NotificationSet
isSelected: { (currentSelection == previewType) }
),
onTap: { [weak self] in
storage.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
db[.preferencesNotificationPreviewType] = previewType
}
@ -70,6 +67,6 @@ class NotificationContentViewModel: SessionTableViewModel<NoNav, NotificationSet
}
.removeDuplicates()
.handleEvents(didFail: { SNLog("[NotificationContentViewModel] Observation failed with error: \($0)") })
.publisher(in: storage, scheduling: scheduler)
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
.mapToSessionTableViewData(for: self)
}

View File

@ -39,6 +39,18 @@ class NotificationSettingsViewModel: SessionTableViewModel<NoNav, NotificationSe
case content
}
private let dependencies: Dependencies
// MARK: - Initialization
init(
using dependencies: Dependencies = Dependencies()
) {
self.dependencies = dependencies
super.init()
}
// MARK: - Content
private struct State: Equatable {
@ -72,7 +84,7 @@ class NotificationSettingsViewModel: SessionTableViewModel<NoNav, NotificationSe
}
.removeDuplicates()
.handleEvents(didFail: { SNLog("[NotificationSettingsViewModel] Observation failed with error: \($0)") })
.publisher(in: Storage.shared)
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
.manualRefreshFrom(forcedRefresh)
.map { dbState -> State in
State(
@ -83,7 +95,7 @@ class NotificationSettingsViewModel: SessionTableViewModel<NoNav, NotificationSe
)
}
.withPrevious()
.map { (previous: State?, current: State) -> [SectionModel] in
.map { [dependencies] (previous: State?, current: State) -> [SectionModel] in
return [
SectionModel(
model: .strategy,
@ -154,7 +166,7 @@ class NotificationSettingsViewModel: SessionTableViewModel<NoNav, NotificationSe
)
),
onTap: {
Storage.shared.write { db in
dependencies[singleton: .storage].write { db in
db[.playNotificationSoundInForeground] = !db[.playNotificationSoundInForeground]
}
}

View File

@ -22,14 +22,21 @@ class NotificationSoundViewModel: SessionTableViewModel<NotificationSoundViewMod
// FIXME: Remove `threadId` once we ditch the per-thread notification sound
private let threadId: String?
private let dependencies: Dependencies
private var audioPlayer: OWSAudioPlayer?
private var storedSelection: Preferences.Sound?
private var currentSelection: CurrentValueSubject<Preferences.Sound?, Never> = CurrentValueSubject(nil)
// MARK: - Initialization
init(threadId: String? = nil) {
init(
threadId: String? = nil,
using dependencies: Dependencies = Dependencies()
) {
self.threadId = threadId
self.dependencies = dependencies
super.init()
}
deinit {
@ -147,7 +154,7 @@ class NotificationSoundViewModel: SessionTableViewModel<NotificationSoundViewMod
}
.removeDuplicates()
.handleEvents(didFail: { SNLog("[NotificationSoundViewModel] Observation failed with error: \($0)") })
.publisher(in: Storage.shared)
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
.mapToSessionTableViewData(for: self)
// MARK: - Functions
@ -157,7 +164,7 @@ class NotificationSoundViewModel: SessionTableViewModel<NotificationSoundViewMod
let threadId: String? = self.threadId
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
guard let threadId: String = threadId else {
db[.defaultNotificationSound] = currentSelection
return

View File

@ -172,7 +172,7 @@ final class NukeDataModal: Modal {
.present(fromViewController: presentedViewController, canCancel: false) { [weak self] _ in
Publishers
.MergeMany(
Storage.shared
dependencies[singleton: .storage]
.read { db -> [(String, HTTP.PreparedRequest<OpenGroupAPI.DeleteInboxResponse>)] in
return try OpenGroup
.filter(OpenGroup.Columns.isActive == true)
@ -268,7 +268,7 @@ final class NukeDataModal: Modal {
///
/// **Note:** This is file as long as this process kills the app, if it doesn't then we need an alternate mechanism to flag that
/// the `JobRunner` is allowed to start it's queues again
JobRunner.stopAndClearPendingJobs()
dependencies[singleton: .jobRunner].stopAndClearPendingJobs()
// Clear the app badge and notifications
AppEnvironment.shared.notificationPresenter.clearAllNotifications()
@ -278,7 +278,7 @@ final class NukeDataModal: Modal {
UserDefaults.removeAll()
// Remove the cached key so it gets re-cached on next access
dependencies.caches.mutate(cache: .general) {
dependencies.mutate(cache: .general) {
$0.encodedPublicKey = nil
$0.recentReactionTimestamps = []
}

View File

@ -113,7 +113,7 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
}
.removeDuplicates()
.handleEvents(didFail: { SNLog("[PrivacySettingsViewModel] Observation failed with error: \($0)") })
.publisher(in: dependencies.storage)
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
.withPrevious()
.map { [dependencies] (previous: State?, current: State) -> [SectionModel] in
return [
@ -148,7 +148,7 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
return
}
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try db.setAndUpdateConfig(
.isScreenLockEnabled,
to: !db[.isScreenLockEnabled],
@ -174,7 +174,7 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
)
),
onTap: { [weak self] in
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try db.setAndUpdateConfig(
.checkForCommunityMessageRequests,
to: !db[.checkForCommunityMessageRequests],
@ -200,7 +200,7 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
)
),
onTap: {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try db.setAndUpdateConfig(
.areReadReceiptsEnabled,
to: !db[.areReadReceiptsEnabled],
@ -258,7 +258,7 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
)
),
onTap: {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try db.setAndUpdateConfig(
.typingIndicatorsEnabled,
to: !db[.typingIndicatorsEnabled],
@ -284,7 +284,7 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
)
),
onTap: {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try db.setAndUpdateConfig(
.areLinkPreviewsEnabled,
to: !db[.areLinkPreviewsEnabled],
@ -322,7 +322,7 @@ class PrivacySettingsViewModel: SessionTableViewModel<PrivacySettingsViewModel.N
onConfirm: { _ in Permissions.requestMicrophonePermissionIfNeeded() }
),
onTap: {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try db.setAndUpdateConfig(
.areCallsEnabled,
to: !db[.areCallsEnabled],

View File

@ -122,7 +122,7 @@ final class SeedModal: Modal {
mnemonicLabel.pin(to: mnemonicLabelContainer, withInset: isIPhone6OrSmaller ? 4 : Values.smallSpacing)
// Mark seed as viewed
Storage.shared.writeAsync { db in db[.hasViewedSeed] = true }
Dependencies()[singleton: .storage].writeAsync { db in db[.hasViewedSeed] = true }
}
// MARK: - Interaction

View File

@ -69,6 +69,7 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
// MARK: - Variables
private let userSessionId: String
private let dependencies: Dependencies
private lazy var imagePickerHandler: ImagePickerHandler = ImagePickerHandler(
onTransition: { [weak self] in self?.transitionToScreen($0, transitionType: $1) },
onImageDataPicked: { [weak self] resultImageData in
@ -87,9 +88,12 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
// MARK: - Initialization
override init() {
self.userSessionId = getUserHexEncodedPublicKey()
self.oldDisplayName = Profile.fetchOrCreateCurrentUser().name
init(
using dependencies: Dependencies = Dependencies()
) {
self.userSessionId = getUserHexEncodedPublicKey(using: dependencies)
self.oldDisplayName = Profile.fetchOrCreateCurrentUser(using: dependencies).name
self.dependencies = dependencies
super.init()
}
@ -482,7 +486,7 @@ class SettingsViewModel: SessionTableViewModel<SettingsViewModel.NavButton, Sett
}
.removeDuplicates()
.handleEvents(didFail: { SNLog("[SettingsViewModel] Observation failed with error: \($0)") })
.publisher(in: Storage.shared)
.publisher(in: dependencies[singleton: .storage], scheduling: dependencies[singleton: .scheduler])
.mapToSessionTableViewData(for: self)
public override var footerView: AnyPublisher<UIView?, Never> {

View File

@ -167,7 +167,7 @@ class ScreenLockUI {
// It's not safe to access OWSScreenLock.isScreenLockEnabled
// until the app is ready.
AppReadiness.runNowOrWhenAppWillBecomeReady { [weak self] in
self?.isScreenLockLocked = Storage.shared[.isScreenLockEnabled]
self?.isScreenLockLocked = Dependencies()[singleton: .storage][.isScreenLockEnabled]
self?.ensureUI()
}
}
@ -184,7 +184,7 @@ class ScreenLockUI {
Logger.verbose("tryToActivateScreenLockUponBecomingActive NO 0")
return
}
guard Storage.shared[.isScreenLockEnabled] else {
guard Dependencies()[singleton: .storage][.isScreenLockEnabled] else {
// Screen lock is not enabled.
Logger.verbose("tryToActivateScreenLockUponBecomingActive NO 1")
return;
@ -372,7 +372,7 @@ class ScreenLockUI {
return;
}
self.isScreenLockLocked = Storage.shared[.isScreenLockEnabled]
self.isScreenLockLocked = Dependencies()[singleton: .storage][.isScreenLockEnabled]
// NOTE: this notifications fires _before_ applicationDidBecomeActive,
// which is desirable. Don't assume that though; call ensureUI

View File

@ -93,7 +93,7 @@ class SessionTableViewController<NavItemId: Equatable, Section: SessionTableSect
init(viewModel: SessionTableViewModel<NavItemId, Section, SettingItem>) {
self.viewModel = viewModel
Storage.shared.addObserver(viewModel.pagedDataObserver)
Dependencies()[singleton: .storage].addObserver(viewModel.pagedDataObserver)
super.init(nibName: nil, bundle: nil)
}

View File

@ -20,7 +20,7 @@ public final class BackgroundPoller {
[pollForMessages(using: dependencies)]
.appending(contentsOf: pollForClosedGroupMessages(using: dependencies))
.appending(
contentsOf: Storage.shared
contentsOf: dependencies[singleton: .storage]
.read { db in
/// The default room promise creates an OpenGroup with an empty `roomToken` value, we
/// don't want to start a poller for this as the user hasn't actually joined a room
@ -95,7 +95,7 @@ public final class BackgroundPoller {
) -> [AnyPublisher<Void, Error>] {
// Fetch all closed groups (excluding any don't contain the current user as a
// GroupMemeber as the user is no longer a member of those)
return Storage.shared
return dependencies[singleton: .storage]
.read { db in
try ClosedGroup
.select(.threadId)

View File

@ -63,7 +63,7 @@ final class IP2Country {
}
@discardableResult func populateCacheIfNeeded() -> Bool {
guard let pathToDisplay: [Snode] = OnionRequestAPI.paths.first else { return false }
guard let pathToDisplay: [Snode] = Dependencies()[cache: .onionRequestAPI].paths.first else { return false }
countryNamesCache.mutate { [weak self] cache in
pathToDisplay.forEach { snode in

View File

@ -48,7 +48,8 @@ public extension UIContextualAction {
indexPath: IndexPath,
tableView: UITableView,
threadViewModel: SessionThreadViewModel,
viewController: UIViewController?
viewController: UIViewController?,
using dependencies: Dependencies = Dependencies()
) -> [UIContextualAction]? {
guard !actions.isEmpty else { return nil }
@ -128,7 +129,9 @@ public extension UIContextualAction {
) { _, _, completionHandler in
switch threadViewModel.threadId {
case SessionThreadViewModel.messageRequestsSectionId:
Storage.shared.write { db in db[.hasHiddenMessageRequests] = true }
dependencies[singleton: .storage].write { db in
db[.hasHiddenMessageRequests] = true
}
completionHandler(true)
default:
@ -159,7 +162,7 @@ public extension UIContextualAction {
cancelStyle: .alert_text,
dismissOnConfirm: true,
onConfirm: { _ in
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try SessionThread.deleteOrLeave(
db,
threadId: threadViewModel.threadId,
@ -207,7 +210,7 @@ public extension UIContextualAction {
// Delay the change to give the cell "unswipe" animation some time to complete
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + unswipeAnimationDelay) {
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try SessionThread
.filter(id: threadViewModel.threadId)
.updateAllAndConfig(
@ -247,7 +250,7 @@ public extension UIContextualAction {
// Delay the change to give the cell "unswipe" animation some time to complete
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + unswipeAnimationDelay) {
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
let currentValue: TimeInterval? = try SessionThread
.filter(id: threadViewModel.threadId)
.select(.mutedUntilTimestamp)
@ -308,7 +311,7 @@ public extension UIContextualAction {
// Delay the change to give the cell "unswipe" animation some time to complete
DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + unswipeAnimationDelay) {
Storage.shared
dependencies[singleton: .storage]
.writePublisher { db in
// Create the contact if it doesn't exist
try Contact
@ -412,7 +415,7 @@ public extension UIContextualAction {
cancelStyle: .alert_text,
dismissOnConfirm: true,
onConfirm: { _ in
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try SessionThread.deleteOrLeave(
db,
threadId: threadViewModel.threadId,
@ -510,7 +513,7 @@ public extension UIContextualAction {
cancelStyle: .alert_text,
dismissOnConfirm: true,
onConfirm: { _ in
Storage.shared.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
try SessionThread.deleteOrLeave(
db,
threadId: threadViewModel.threadId,

View File

@ -176,7 +176,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
}
}
dependencies.storage
dependencies[singleton: .storage]
.writePublisher { db in
try MessageSender
.preparedSendData(
@ -220,7 +220,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
let uuid: String = self.uuid
let mediaConstraints: RTCMediaConstraints = mediaConstraints(false)
return dependencies.storage
return dependencies[singleton: .storage]
.readPublisher { db -> SessionThread in
guard let thread: SessionThread = try? SessionThread.fetchOne(db, id: sessionId) else {
throw WebRTCSessionError.noThread
@ -247,7 +247,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
}
}
dependencies.storage
dependencies[singleton: .storage]
.writePublisher { db in
try MessageSender
.preparedSendData(
@ -300,7 +300,7 @@ public final class WebRTCSession : NSObject, RTCPeerConnectionDelegate {
// Empty the queue
self.queuedICECandidates.removeAll()
dependencies.storage
dependencies[singleton: .storage]
.writePublisher { db in
guard let thread: SessionThread = try SessionThread.fetchOne(db, id: contactSessionId) else {
throw WebRTCSessionError.noThread

View File

@ -40,24 +40,30 @@ public enum SNMessagingKit: MigratableTarget { // Just to make the external API
)
}
public static func configure() {
public static func configure(using dependencies: Dependencies) {
// Configure the job executors
JobRunner.setExecutor(DisappearingMessagesJob.self, for: .disappearingMessages)
JobRunner.setExecutor(FailedMessageSendsJob.self, for: .failedMessageSends)
JobRunner.setExecutor(FailedAttachmentDownloadsJob.self, for: .failedAttachmentDownloads)
JobRunner.setExecutor(UpdateProfilePictureJob.self, for: .updateProfilePicture)
JobRunner.setExecutor(RetrieveDefaultOpenGroupRoomsJob.self, for: .retrieveDefaultOpenGroupRooms)
JobRunner.setExecutor(GarbageCollectionJob.self, for: .garbageCollection)
JobRunner.setExecutor(MessageSendJob.self, for: .messageSend)
JobRunner.setExecutor(MessageReceiveJob.self, for: .messageReceive)
JobRunner.setExecutor(NotifyPushServerJob.self, for: .notifyPushServer)
JobRunner.setExecutor(SendReadReceiptsJob.self, for: .sendReadReceipts)
JobRunner.setExecutor(AttachmentUploadJob.self, for: .attachmentUpload)
JobRunner.setExecutor(GroupLeavingJob.self, for: .groupLeaving)
JobRunner.setExecutor(AttachmentDownloadJob.self, for: .attachmentDownload)
JobRunner.setExecutor(ConfigurationSyncJob.self, for: .configurationSync)
JobRunner.setExecutor(ConfigMessageReceiveJob.self, for: .configMessageReceive)
JobRunner.setExecutor(ExpirationUpdateJob.self, for: .expirationUpdate)
JobRunner.setExecutor(GetExpirationJob.self, for: .getExpiration)
let executors: [Job.Variant: JobExecutor.Type] = [
.disappearingMessages: DisappearingMessagesJob.self,
.failedMessageSends: FailedMessageSendsJob.self,
.failedAttachmentDownloads: FailedAttachmentDownloadsJob.self,
.updateProfilePicture: UpdateProfilePictureJob.self,
.retrieveDefaultOpenGroupRooms: RetrieveDefaultOpenGroupRoomsJob.self,
.garbageCollection: GarbageCollectionJob.self,
.messageSend: MessageSendJob.self,
.messageReceive: MessageReceiveJob.self,
.notifyPushServer: NotifyPushServerJob.self,
.sendReadReceipts: SendReadReceiptsJob.self,
.attachmentUpload: AttachmentUploadJob.self,
.groupLeaving: GroupLeavingJob.self,
.attachmentDownload: AttachmentDownloadJob.self,
.configurationSync: ConfigurationSyncJob.self,
.configMessageReceive: ConfigMessageReceiveJob.self,
.expirationUpdate: ExpirationUpdateJob.self,
.getExpiration: GetExpirationJob.self
]
executors.forEach { variant, executor in
dependencies[singleton: .jobRunner].setExecutor(executor, for: variant)
}
}
}

View File

@ -378,6 +378,6 @@ enum _001_InitialSetupMigration: Migration {
t.column(.timestampMs, .integer).notNull()
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -51,6 +51,6 @@ enum _002_SetupStandardJobs: Migration {
).migrationSafeInserted(db)
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -16,7 +16,7 @@ enum _003_YDBToGRDBMigration: Migration {
guard
!SNUtilitiesKit.isRunningTests &&
Identity.userExists(db)
else { return Storage.update(progress: 1, for: self, in: target) }
else { return Storage.update(progress: 1, for: self, in: target, using: dependencies) }
SNLogNotTests("[Migration Error] Attempted to perform legacy migation")
throw StorageError.migrationNoLongerSupported

View File

@ -13,6 +13,6 @@ enum _004_RemoveLegacyYDB: Migration {
static let minExpectedRunDuration: TimeInterval = 0.1
static func migrate(_ db: Database, using dependencies: Dependencies) throws {
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -20,6 +20,6 @@ enum _005_FixDeletedMessageReadState: Migration {
)
.updateAll(db, Interaction.Columns.wasRead.set(to: true))
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -25,6 +25,6 @@ enum _006_FixHiddenModAdminSupport: Migration {
_ = try OpenGroup
.updateAll(db, OpenGroup.Columns.infoUpdates.set(to: 0))
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -32,6 +32,6 @@ enum _007_HomeQueryOptimisationIndexes: Migration {
]
)
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -37,6 +37,6 @@ enum _008_EmojiReacts: Migration {
t.uniqueKey([.interactionId, .emoji, .authorId])
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -22,6 +22,6 @@ enum _009_OpenGroupPermission: Migration {
_ = try OpenGroup
.updateAll(db, OpenGroup.Columns.infoUpdates.set(to: 0))
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -28,6 +28,6 @@ enum _010_AddThreadIdToFTS: Migration {
t.column(Interaction.Columns.threadId.name)
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -29,6 +29,6 @@ enum _011_AddPendingReadReceipts: Migration {
t.primaryKey([.threadId, .interactionTimestampMs])
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -24,6 +24,6 @@ enum _012_AddFTSIfNeeded: Migration {
}
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -243,6 +243,6 @@ enum _013_SessionUtilChanges: Migration {
}
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -15,7 +15,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
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
Storage.update(progress: 1, for: self, in: target, using: dependencies)
return
}
@ -33,7 +33,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
// MARK: - UserProfile Config Dump
try dependencies.caches[.sessionUtil]
try dependencies[cache: .sessionUtil]
.config(for: .userProfile, publicKey: userPublicKey)
.mutate { config in
try SessionUtil.update(
@ -64,7 +64,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
// MARK: - Contact Config Dump
try dependencies.caches[.sessionUtil]
try dependencies[cache: .sessionUtil]
.config(for: .contacts, publicKey: userPublicKey)
.mutate { config in
// Exclude Note to Self, community, group and outgoing blinded message requests
@ -130,7 +130,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
// MARK: - ConvoInfoVolatile Config Dump
try dependencies.caches[.sessionUtil]
try dependencies[cache: .sessionUtil]
.config(for: .convoInfoVolatile, publicKey: userPublicKey)
.mutate { config in
let volatileThreadInfo: [SessionUtil.VolatileThreadInfo] = SessionUtil.VolatileThreadInfo
@ -155,7 +155,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
// MARK: - UserGroups Config Dump
try dependencies.caches[.sessionUtil]
try dependencies[cache: .sessionUtil]
.config(for: .userGroups, publicKey: userPublicKey)
.mutate { config in
let legacyGroupData: [SessionUtil.LegacyGroupInfo] = try SessionUtil.LegacyGroupInfo.fetchAll(db)
@ -200,7 +200,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
ConfigurationSyncJob.enqueue(db, publicKey: userPublicKey)
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
struct ContactInfo: FetchableRecord, Decodable, ColumnExpressible {

View File

@ -26,7 +26,7 @@ enum _015_BlockCommunityMessageRequests: Migration {
Identity.userExists(db),
(try Setting.exists(db, id: Setting.BoolKey.checkForCommunityMessageRequests.rawValue)) == false
{
let rawBlindedMessageRequestValue: Int32 = try dependencies.caches[.sessionUtil]
let rawBlindedMessageRequestValue: Int32 = try dependencies[cache: .sessionUtil]
.config(for: .userProfile, publicKey: getUserHexEncodedPublicKey(db))
.wrappedValue
.map { config -> Int32 in try SessionUtil.rawBlindedMessageRequestValue(in: config) }
@ -39,6 +39,6 @@ enum _015_BlockCommunityMessageRequests: Migration {
)
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -34,7 +34,7 @@ enum _016_DisappearingMessagesConfiguration: Migration {
// If there isn't already a user account then we can just finish here (there will be no
// threads/configs to update and the configs won't be setup which would cause this to crash
guard Identity.userExists(db) else {
return Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
return Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
// Convenience function to set the disappearing messages type per conversation
@ -78,7 +78,7 @@ enum _016_DisappearingMessagesConfiguration: Migration {
_ = 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
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -27,7 +27,7 @@ enum _017_GroupsRebuildChanges: Migration {
.defaults(to: true)
}
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
Storage.update(progress: 1, for: self, in: target, using: dependencies)
}
}

View File

@ -1071,7 +1071,7 @@ extension Attachment {
let attachmentId: String = self.id
return dependencies.storage
return dependencies[singleton: .storage]
.writePublisher { db -> (HTTP.PreparedRequest<FileUploadResponse>?, String?, Data?, Data?) in
// If the attachment is a downloaded attachment, check if it came from
// the server and if so just succeed immediately (no use re-uploading
@ -1165,7 +1165,7 @@ extension Attachment {
///
/// **Note:** We **MUST** use the `.with` function here to ensure the `isValid` flag is
/// updated correctly
dependencies.storage
dependencies[singleton: .storage]
.writePublisher { db in
try self
.with(
@ -1189,7 +1189,7 @@ extension Attachment {
switch result {
case .finished: break
case .failure:
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try Attachment
.filter(id: attachmentId)
.updateAll(db, Attachment.Columns.state.set(to: Attachment.State.failedUpload))

View File

@ -94,7 +94,7 @@ public extension BlindedIdLookup {
// If we we given a sessionId then validate it is correct and if so save it
if
let sessionId: String = sessionId,
dependencies.crypto.verify(
dependencies[singleton: .crypto].verify(
.sessionId(
sessionId,
matchesBlindedId: blindedId,
@ -118,7 +118,7 @@ public extension BlindedIdLookup {
while let contact: Contact = try contactsThatApprovedMeCursor.next() {
guard
dependencies.crypto.verify(
dependencies[singleton: .crypto].verify(
.sessionId(
contact.id,
matchesBlindedId: blindedId,
@ -160,7 +160,7 @@ public extension BlindedIdLookup {
while let otherLookup: BlindedIdLookup = try blindedIdLookupCursor.next() {
guard
let sessionId: String = otherLookup.sessionId,
dependencies.crypto.verify(
dependencies[singleton: .crypto].verify(
.sessionId(
sessionId,
matchesBlindedId: blindedId,

View File

@ -195,12 +195,13 @@ public extension ClosedGroup {
) throws {
guard !threadIds.isEmpty else { return }
guard let db: Database = db else {
Storage.shared.write { db in
dependencies[singleton: .storage].write { db in
try ClosedGroup.removeKeysAndUnsubscribe(
db,
threadIds: threadIds,
removeGroupData: removeGroupData,
calledFromConfigHandling: calledFromConfigHandling
calledFromConfigHandling: calledFromConfigHandling,
using: dependencies
)
}
return
@ -210,7 +211,7 @@ public extension ClosedGroup {
let userPublicKey: String = getUserHexEncodedPublicKey(db)
threadIds.forEach { threadId in
ClosedGroupPoller.shared.stopPolling(for: threadId)
dependencies[singleton: .closedGroupPoller].stopPolling(for: threadId)
try? PushNotificationAPI
.preparedUnsubscribeFromLegacyGroup(

View File

@ -456,9 +456,11 @@ public struct Interaction: Codable, Identifiable, Equatable, FetchableRecord, Mu
// Start the disappearing messages timer if needed
if self.expiresStartedAtMs != nil {
JobRunner.upsert(
Dependencies()[singleton: .jobRunner].upsert(
db,
job: DisappearingMessagesJob.updateNextRunIfNeeded(db)
job: DisappearingMessagesJob.updateNextRunIfNeeded(db),
canStartJob: true,
using: Dependencies()
)
}
}
@ -544,14 +546,16 @@ public extension Interaction {
// Add the 'DisappearingMessagesJob' if needed - this will update any expiring
// messages `expiresStartedAtMs` values in local database, and create seperate
// jobs updating message expiration
JobRunner.upsert(
dependencies[singleton: .jobRunner].upsert(
db,
job: DisappearingMessagesJob.updateNextRunIfNeeded(
db,
interactionIds: interactionInfo.map { $0.id },
startedAtMs: TimeInterval(SnodeAPI.currentOffsetTimestampMs()),
threadId: threadId
threadId: threadId,
using: dependencies
),
canStartJob: true,
using: dependencies
)
@ -584,15 +588,17 @@ public extension Interaction {
// If we want to send read receipts and it's a contact thread then try to add the
// 'SendReadReceiptsJob' for and unread messages that weren't outgoing
if trySendReadReceipt && threadVariant == .contact {
JobRunner.upsert(
dependencies[singleton: .jobRunner].upsert(
db,
job: SendReadReceiptsJob.createOrUpdateIfNeeded(
db,
threadId: threadId,
interactionIds: interactionInfo
.filter { !$0.wasRead && $0.variant != .standardOutgoing }
.map { $0.id }
.map { $0.id },
using: dependencies
),
canStartJob: true,
using: dependencies
)
}
@ -871,7 +877,7 @@ public extension Interaction {
if let openGroup: OpenGroup = try? OpenGroup.fetchOne(db, id: threadId) {
if
let userEd25519KeyPair: KeyPair = Identity.fetchUserEd25519KeyPair(db),
let blindedKeyPair: KeyPair = dependencies.crypto.generate(
let blindedKeyPair: KeyPair = dependencies[singleton: .crypto].generate(
.blindedKeyPair(serverPublicKey: openGroup.publicKey, edKeyPair: userEd25519KeyPair, using: dependencies)
)
{

View File

@ -211,8 +211,12 @@ public extension LinkPreview {
private static var previewUrlCache: Atomic<NSCache<NSString, NSString>> = Atomic(NSCache())
static func previewUrl(for body: String?, selectedRange: NSRange? = nil) -> String? {
guard Storage.shared[.areLinkPreviewsEnabled] else { return nil }
static func previewUrl(
for body: String?,
selectedRange: NSRange? = nil,
using dependencies: Dependencies = Dependencies()
) -> String? {
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else { return nil }
guard let body: String = body else { return nil }
if let cachedUrl = previewUrlCache.wrappedValue.object(forKey: body as NSString) as String? {
@ -284,20 +288,27 @@ public extension LinkPreview {
}
}
private static func setCachedLinkPreview(_ linkPreviewDraft: LinkPreviewDraft, forPreviewUrl previewUrl: String) {
private static func setCachedLinkPreview(
_ linkPreviewDraft: LinkPreviewDraft,
forPreviewUrl previewUrl: String,
using dependencies: Dependencies = Dependencies()
) {
assert(previewUrl == linkPreviewDraft.urlString)
// Exit early if link previews are not enabled in order to avoid
// tainting the cache.
guard Storage.shared[.areLinkPreviewsEnabled] else { return }
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else { return }
serialQueue.sync {
linkPreviewDraftCache = linkPreviewDraft
}
}
static func tryToBuildPreviewInfo(previewUrl: String?) -> AnyPublisher<LinkPreviewDraft, Error> {
guard Storage.shared[.areLinkPreviewsEnabled] else {
static func tryToBuildPreviewInfo(
previewUrl: String?,
using dependencies: Dependencies = Dependencies()
) -> AnyPublisher<LinkPreviewDraft, Error> {
guard dependencies[singleton: .storage][.areLinkPreviewsEnabled] else {
return Fail(error: LinkPreviewError.featureDisabled)
.eraseToAnyPublisher()
}

View File

@ -213,8 +213,12 @@ public extension Profile {
)
}
static func fetchAllContactProfiles(excluding: Set<String> = [], excludeCurrentUser: Bool = true) -> [Profile] {
return Storage.shared
static func fetchAllContactProfiles(
excluding: Set<String> = [],
excludeCurrentUser: Bool = true,
using dependencies: Dependencies = Dependencies()
) -> [Profile] {
return dependencies[singleton: .storage]
.read { db in
// Sort the contacts by their displayName value
try Profile
@ -228,10 +232,24 @@ public extension Profile {
.defaulting(to: [])
}
static func displayName(_ db: Database? = nil, id: ID, threadVariant: SessionThread.Variant = .contact, customFallback: String? = nil) -> String {
static func displayName(
_ db: Database? = nil,
id: ID,
threadVariant: SessionThread.Variant = .contact,
customFallback: String? = nil,
using dependencies: Dependencies = Dependencies()
) -> String {
guard let db: Database = db else {
return Storage.shared
.read { db in displayName(db, id: id, threadVariant: threadVariant, customFallback: customFallback) }
return dependencies[singleton: .storage]
.read { db in
displayName(
db,
id: id,
threadVariant: threadVariant,
customFallback: customFallback,
using: dependencies
)
}
.defaulting(to: (customFallback ?? id))
}
@ -241,9 +259,16 @@ public extension Profile {
return (existingDisplayName ?? (customFallback ?? id))
}
static func displayNameNoFallback(_ db: Database? = nil, id: ID, threadVariant: SessionThread.Variant = .contact) -> String? {
static func displayNameNoFallback(
_ db: Database? = nil,
id: ID,
threadVariant: SessionThread.Variant = .contact,
using dependencies: Dependencies = Dependencies()
) -> String? {
guard let db: Database = db else {
return Storage.shared.read { db in displayNameNoFallback(db, id: id, threadVariant: threadVariant) }
return dependencies[singleton: .storage].read { db in
displayNameNoFallback(db, id: id, threadVariant: threadVariant, using: dependencies)
}
}
return (try? Profile.fetchOne(db, id: id))?
@ -278,7 +303,7 @@ public extension Profile {
let userPublicKey: String = getUserHexEncodedPublicKey(db, using: dependencies)
guard let db: Database = db else {
return dependencies.storage
return dependencies[singleton: .storage]
.read { db in fetchOrCreateCurrentUser(db, using: dependencies) }
.defaulting(to: defaultFor(userPublicKey))
}

View File

@ -548,7 +548,7 @@ public extension SessionThread {
) -> String? {
guard threadVariant == .community else { return nil }
guard let db: Database = db else {
return dependencies.storage.read { db in
return dependencies[singleton: .storage].read { db in
getUserHexEncodedBlindedKey(
db,
threadId: threadId,
@ -584,7 +584,7 @@ public extension SessionThread {
guard capabilities.isEmpty || capabilities.contains(.blind) else { return nil }
let blindedKeyPair: KeyPair? = dependencies.crypto.generate(
let blindedKeyPair: KeyPair? = dependencies[singleton: .crypto].generate(
.blindedKeyPair(serverPublicKey: openGroupInfo.publicKey, edKeyPair: userEdKeyPair, using: dependencies)
)

View File

@ -23,7 +23,7 @@ public enum AttachmentDownloadJob: JobExecutor {
let threadId: String = job.threadId,
let detailsData: Data = job.details,
let details: Details = try? JSONDecoder().decode(Details.self, from: detailsData),
let attachment: Attachment = Storage.shared
let attachment: Attachment = dependencies[singleton: .storage]
.read({ db in try Attachment.fetchOne(db, id: details.attachmentId) })
else {
failure(job, JobRunnerError.missingRequiredDetails, true, dependencies)
@ -42,7 +42,7 @@ public enum AttachmentDownloadJob: JobExecutor {
// the same attachment multiple times at the same time (it also adds a "clean up" mechanism
// if an attachment ends up stuck in a "downloading" state incorrectly
guard attachment.state != .downloading else {
let otherCurrentJobAttachmentIds: Set<String> = dependencies.jobRunner
let otherCurrentJobAttachmentIds: Set<String> = dependencies[singleton: .jobRunner]
.jobInfoFor(state: .running, variant: .attachmentDownload)
.filter { key, _ in key != job.id }
.values
@ -58,7 +58,7 @@ public enum AttachmentDownloadJob: JobExecutor {
// then we should update the state of the attachment to be failed to avoid having attachments
// appear in an endlessly downloading state
if !otherCurrentJobAttachmentIds.contains(attachment.id) {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
_ = try Attachment
.filter(id: attachment.id)
.updateAll(db, Attachment.Columns.state.set(to: Attachment.State.failedDownload))
@ -76,7 +76,7 @@ public enum AttachmentDownloadJob: JobExecutor {
}
// Update to the 'downloading' state (no need to update the 'attachment' instance)
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try Attachment
.filter(id: attachment.id)
.updateAll(db, Attachment.Columns.state.set(to: Attachment.State.downloading))
@ -94,7 +94,7 @@ public enum AttachmentDownloadJob: JobExecutor {
let fileId: String = Attachment.fileId(for: downloadUrl)
else { throw AttachmentDownloadError.invalidUrl }
return Storage.shared
return dependencies[singleton: .storage]
.readPublisher { db -> HTTP.PreparedRequest<Data>? in
try OpenGroup.fetchOne(db, id: threadId)
.map { openGroup in
@ -164,7 +164,7 @@ public enum AttachmentDownloadJob: JobExecutor {
///
/// **Note:** We **MUST** use the `'with()` function here as it will update the
/// `isValid` and `duration` values based on the downloaded data and the state
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
_ = try attachment
.with(
state: .downloaded,
@ -209,7 +209,7 @@ public enum AttachmentDownloadJob: JobExecutor {
///
/// **Note:** We **MUST** use the `'with()` function here as it will update the
/// `isValid` and `duration` values based on the downloaded data and the state
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
_ = try Attachment
.filter(id: attachment.id)
.updateAll(db, Attachment.Columns.state.set(to: targetState))

View File

@ -24,7 +24,7 @@ public enum AttachmentUploadJob: JobExecutor {
let interactionId: Int64 = job.interactionId,
let detailsData: Data = job.details,
let details: Details = try? JSONDecoder().decode(Details.self, from: detailsData),
let (attachment, openGroup): (Attachment, OpenGroup?) = dependencies.storage.read({ db in
let (attachment, openGroup): (Attachment, OpenGroup?) = dependencies[singleton: .storage].read({ db in
guard let attachment: Attachment = try Attachment.fetchOne(db, id: details.attachmentId) else {
return nil
}
@ -38,7 +38,7 @@ public enum AttachmentUploadJob: JobExecutor {
// If the original interaction no longer exists then don't bother uploading the attachment (ie. the
// message was deleted before it even got sent)
guard dependencies.storage.read({ db in try Interaction.exists(db, id: interactionId) }) == true else {
guard dependencies[singleton: .storage].read({ db in try Interaction.exists(db, id: interactionId) }) == true else {
SNLog("[AttachmentUploadJob] Failed due to missing interaction")
return failure(job, StorageError.objectNotFound, true, dependencies)
}
@ -52,7 +52,7 @@ public enum AttachmentUploadJob: JobExecutor {
// If this upload is related to sending a message then trigger the 'handleMessageWillSend' logic
// as if this is a retry the logic wouldn't run until after the upload has completed resulting in
// a potentially incorrect delivery status
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
guard
let sendJob: Job = try Job.fetchOne(db, id: details.messageSendJobId),
let sendJobDetails: Data = sendJob.details,
@ -82,7 +82,7 @@ public enum AttachmentUploadJob: JobExecutor {
// If this upload is related to sending a message then trigger the
// 'handleFailedMessageSend' logic as we want to ensure the message
// has the correct delivery status
dependencies.storage.read { db in
dependencies[singleton: .storage].read { db in
guard
let sendJob: Job = try Job.fetchOne(db, id: details.messageSendJobId),
let sendJobDetails: Data = sendJob.details,

View File

@ -25,7 +25,7 @@ public enum ConfigMessageReceiveJob: JobExecutor {
let removeDependencyOnMessageReceiveJobs: () -> () = {
guard let jobId: Int64 = job.id else { return }
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try JobDependencies
.filter(JobDependencies.Columns.dependantId == jobId)
.joining(
@ -55,7 +55,7 @@ public enum ConfigMessageReceiveJob: JobExecutor {
let sharedConfigMessages: [SharedConfigMessage] = details.messages
.compactMap { $0.message as? SharedConfigMessage }
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
// Send any SharedConfigMessages to the SessionUtil to handle it
do {
try SessionUtil.handleConfigMessages(

View File

@ -30,8 +30,7 @@ public enum ConfigurationSyncJob: JobExecutor {
// between the jobs we just continue to defer the subsequent job while the first one is running in
// order to prevent multiple configurationSync jobs with the same target from running at the same time
guard
dependencies
.jobRunner
dependencies[singleton: .jobRunner]
.jobInfoFor(state: .running, variant: .configurationSync)
.filter({ key, info in
key != job.id && // Exclude this job
@ -41,7 +40,7 @@ public enum ConfigurationSyncJob: JobExecutor {
else {
// Defer the job to run 'maxRunFrequency' from when this one ran (if we don't it'll try start
// it again immediately which is pointless)
let updatedJob: Job? = dependencies.storage.write { db in
let updatedJob: Job? = dependencies[singleton: .storage].write { db in
try job
.with(nextRunTimestamp: dependencies.dateNow.timeIntervalSince1970 + maxRunFrequency)
.saved(db)
@ -56,7 +55,7 @@ public enum ConfigurationSyncJob: JobExecutor {
// fresh install due to the migrations getting run)
guard
let publicKey: String = job.threadId,
let pendingConfigChanges: [SessionUtil.OutgoingConfResult] = dependencies.storage
let pendingConfigChanges: [SessionUtil.OutgoingConfResult] = dependencies[singleton: .storage]
.read(using: dependencies, { db in
try SessionUtil.pendingChanges(db, publicKey: publicKey, using: dependencies)
})
@ -73,7 +72,7 @@ public enum ConfigurationSyncJob: JobExecutor {
}
// Identify the destination and merge all obsolete hashes into a single set
let destination: Message.Destination = (publicKey == getUserHexEncodedPublicKey() ?
let destination: Message.Destination = (publicKey == getUserHexEncodedPublicKey(using: dependencies) ?
Message.Destination.contact(publicKey: publicKey) :
Message.Destination.closedGroup(groupPublicKey: publicKey)
)
@ -84,7 +83,7 @@ public enum ConfigurationSyncJob: JobExecutor {
let jobStartTimestamp: TimeInterval = dependencies.dateNow.timeIntervalSince1970
SNLog("[ConfigurationSyncJob] For \(publicKey) started with \(pendingConfigChanges.count) change\(pendingConfigChanges.count == 1 ? "" : "s")")
dependencies.storage
dependencies[singleton: .storage]
.readPublisher { db in
try pendingConfigChanges.map { change -> MessageSender.PreparedSendData in
try MessageSender.preparedSendData(
@ -152,7 +151,7 @@ public enum ConfigurationSyncJob: JobExecutor {
var shouldFinishCurrentJob: Bool = false
// Lastly we need to save the updated dumps to the database
let updatedJob: Job? = dependencies.storage.write { db in
let updatedJob: Job? = dependencies[singleton: .storage].write { db in
// Save the updated dumps to the database
try configDumps.forEach { try $0.save(db) }
@ -173,7 +172,7 @@ public enum ConfigurationSyncJob: JobExecutor {
{
// If the next job isn't currently running then delay it's start time
// until the 'nextRunTimestamp'
if !dependencies.jobRunner.isCurrentlyRunning(existingJob) {
if !dependencies[singleton: .jobRunner].isCurrentlyRunning(existingJob) {
_ = try existingJob
.with(nextRunTimestamp: nextRunTimestamp)
.saved(db)
@ -204,7 +203,7 @@ public extension ConfigurationSyncJob {
dependencies: Dependencies = Dependencies()
) {
// Upsert a config sync job if needed
dependencies.jobRunner.upsert(
dependencies[singleton: .jobRunner].upsert(
db,
job: ConfigurationSyncJob.createIfNeeded(db, publicKey: publicKey, using: dependencies),
canStartJob: true,
@ -222,7 +221,7 @@ public extension ConfigurationSyncJob {
///
/// **Note:** Jobs with different `threadId` values can run concurrently
guard
dependencies.jobRunner
dependencies[singleton: .jobRunner]
.jobInfoFor(state: .running, variant: .configurationSync)
.filter({ _, info in info.threadId == publicKey })
.isEmpty,

View File

@ -24,7 +24,7 @@ public enum DisappearingMessagesJob: JobExecutor {
var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(label: #function)
var numDeleted: Int = -1
let updatedJob: Job? = Storage.shared.write { db in
let updatedJob: Job? = dependencies[singleton: .storage].write { db in
numDeleted = try Interaction
.filter(Interaction.Columns.expiresStartedAtMs != nil)
.filter((Interaction.Columns.expiresStartedAtMs + (Interaction.Columns.expiresInSeconds * 1000)) <= timestampNowMs)
@ -79,7 +79,12 @@ public extension DisappearingMessagesJob {
.saved(db)
}
static func updateNextRunIfNeeded(_ db: Database, lastReadTimestampMs: Int64, threadId: String) {
static func updateNextRunIfNeeded(
_ db: Database,
lastReadTimestampMs: Int64,
threadId: String,
using dependencies: Dependencies
) {
struct ExpirationInfo: Codable, Hashable, FetchableRecord {
let expiresInSeconds: TimeInterval
let serverHash: String
@ -105,7 +110,7 @@ public extension DisappearingMessagesJob {
let startedAtTimestampMs: Double = Double(SnodeAPI.currentOffsetTimestampMs())
JobRunner.add(
dependencies[singleton: .jobRunner].add(
db,
job: Job(
variant: .getExpiration,
@ -115,11 +120,19 @@ public extension DisappearingMessagesJob {
expirationInfo: expirationInfo,
startedAtTimestampMs: startedAtTimestampMs
)
)
),
canStartJob: true,
using: dependencies
)
}
@discardableResult static func updateNextRunIfNeeded(_ db: Database, interactionIds: [Int64], startedAtMs: Double, threadId: String) -> Job? {
@discardableResult static func updateNextRunIfNeeded(
_ db: Database,
interactionIds: [Int64],
startedAtMs: Double,
threadId: String,
using dependencies: Dependencies
) -> Job? {
struct ExpirationInfo: Codable, Hashable, FetchableRecord {
let id: Int64
let expiresInSeconds: TimeInterval
@ -159,7 +172,7 @@ public extension DisappearingMessagesJob {
interactionExpirationInfosByExpiresInSeconds.forEach { expiresInSeconds, expirationInfos in
let expirationTimestampMs: Int64 = Int64(startedAtMs + expiresInSeconds * 1000)
JobRunner.add(
dependencies[singleton: .jobRunner].add(
db,
job: Job(
variant: .expirationUpdate,
@ -169,14 +182,21 @@ public extension DisappearingMessagesJob {
serverHashes: expirationInfos.map { $0.serverHash },
expirationTimestampMs: expirationTimestampMs
)
)
),
canStartJob: true,
using: dependencies
)
}
return updateNextRunIfNeeded(db)
}
@discardableResult static func updateNextRunIfNeeded(_ db: Database, interaction: Interaction, startedAtMs: Double) -> Job? {
@discardableResult static func updateNextRunIfNeeded(
_ db: Database,
interaction: Interaction,
startedAtMs: Double,
using dependencies: Dependencies
) -> Job? {
guard interaction.isExpiringMessage else { return nil }
// Don't clobber if multiple actions simultaneously triggered expiration
@ -189,7 +209,13 @@ public extension DisappearingMessagesJob {
throw StorageError.objectNotFound
}
return updateNextRunIfNeeded(db, interactionIds: [interactionId], startedAtMs: startedAtMs, threadId: interaction.threadId)
return updateNextRunIfNeeded(
db,
interactionIds: [interactionId],
startedAtMs: startedAtMs,
threadId: interaction.threadId,
using: dependencies
)
}
catch {
SNLog("[DisappearingMessagesJob] Failed to update the expiring messages timer on an interaction")

View File

@ -62,7 +62,7 @@ public enum ExpirationUpdateJob: JobExecutor {
receiveValue: { unchangedMessages in
guard !unchangedMessages.isEmpty else { return }
dependencies.storage.writeAsync(using: dependencies) { db in
dependencies[singleton: .storage].writeAsync(using: dependencies) { db in
try unchangedMessages.forEach { updatedExpiry, hashes in
try hashes.forEach { hash in
guard

View File

@ -21,7 +21,7 @@ public enum FailedAttachmentDownloadsJob: JobExecutor {
var changeCount: Int = -1
// Update all 'sending' message states to 'failed'
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
changeCount = try Attachment
.filter(Attachment.Columns.state == Attachment.State.downloading)
.updateAll(db, Attachment.Columns.state.set(to: Attachment.State.failedDownload))

View File

@ -21,7 +21,7 @@ public enum FailedMessageSendsJob: JobExecutor {
var attachmentChangeCount: Int = -1
// Update all 'sending' message states to 'failed'
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
let sendChangeCount: Int = try RecipientState
.filter(RecipientState.Columns.state == RecipientState.State.sending)
.updateAll(db, RecipientState.Columns.state.set(to: RecipientState.State.failed))

View File

@ -40,7 +40,7 @@ public enum GarbageCollectionJob: JobExecutor {
/// app at about the same time every day will trigger the garbage collection) - since this runs when the app becomes active we
/// want to prevent it running to frequently (the app becomes active if a system alert, the notification center or the control panel
/// are shown)
let lastGarbageCollection: Date = dependencies.standardUserDefaults[.lastGarbageCollection]
let lastGarbageCollection: Date = dependencies[singleton: .standardUserDefaults][.lastGarbageCollection]
.defaulting(to: Date.distantPast)
let finalTypesToCollect: Set<Types> = {
guard
@ -58,7 +58,7 @@ public enum GarbageCollectionJob: JobExecutor {
return typesToCollect.asSet()
}()
dependencies.storage.writeAsync(
dependencies[singleton: .storage].writeAsync(
updates: { db in
/// Remove any typing indicators
if finalTypesToCollect.contains(.threadTypingIndicators) {
@ -345,7 +345,7 @@ public enum GarbageCollectionJob: JobExecutor {
let profileAvatarFilenames: Set<String>
}
let maybeFileInfo: FileInfo? = Storage.shared.read { db -> FileInfo in
let maybeFileInfo: FileInfo? = dependencies[singleton: .storage].read { db -> FileInfo in
var attachmentLocalRelativePaths: Set<String> = []
var profileAvatarFilenames: Set<String> = []
@ -461,7 +461,7 @@ public enum GarbageCollectionJob: JobExecutor {
// If we did a full collection then update the 'lastGarbageCollection' date to
// prevent a full collection from running again in the next 23 hours
if job.behaviour == .recurringOnActive && dependencies.dateNow.timeIntervalSince(lastGarbageCollection) > (23 * 60 * 60) {
dependencies.standardUserDefaults[.lastGarbageCollection] = dependencies.dateNow
dependencies[singleton: .standardUserDefaults][.lastGarbageCollection] = dependencies.dateNow
}
success(job, false, dependencies)

View File

@ -29,7 +29,7 @@ public enum GetExpirationJob: JobExecutor {
return
}
let expirationInfo: [String: TimeInterval] = dependencies.storage
let expirationInfo: [String: TimeInterval] = dependencies[singleton: .storage]
.read(using: dependencies) { db -> [String: TimeInterval] in
details
.expirationInfo
@ -75,7 +75,7 @@ public enum GetExpirationJob: JobExecutor {
let hashesToUseDefault: Set<String> = Set(expirationInfo.keys)
.subtracting(serverSpecifiedExpirationStartTimesMs.keys)
dependencies.storage.write(using: dependencies) { db in
dependencies[singleton: .storage].write(using: dependencies) { db in
try serverSpecifiedExpirationStartTimesMs.forEach { hash, expiresStartedAtMs in
try Interaction
.filter(Interaction.Columns.serverHash == hash)
@ -93,7 +93,7 @@ public enum GetExpirationJob: JobExecutor {
Interaction.Columns.expiresStartedAtMs.set(to: details.startedAtTimestampMs)
)
dependencies.jobRunner
dependencies[singleton: .jobRunner]
.upsert(
db,
job: DisappearingMessagesJob.updateNextRunIfNeeded(db),
@ -103,7 +103,7 @@ public enum GetExpirationJob: JobExecutor {
}
guard hashesToUseDefault.isEmpty else {
let updatedJob: Job? = dependencies.storage.write(using: dependencies) { db in
let updatedJob: Job? = dependencies[singleton: .storage].write(using: dependencies) { db in
try job
.with(nextRunTimestamp: dependencies.dateNow.timeIntervalSince1970 + minRunFrequency)
.saved(db)

View File

@ -22,7 +22,7 @@ public enum GroupInviteMemberJob: JobExecutor {
guard
let threadId: String = job.threadId,
let detailsData: Data = job.details,
let currentInfo: (groupName: String, adminProfile: Profile) = dependencies.storage.read({ db in
let currentInfo: (groupName: String, adminProfile: Profile) = dependencies[singleton: .storage].read({ db in
let maybeGroupName: String? = try ClosedGroup
.filter(id: threadId)
.select(.name)

View File

@ -32,7 +32,7 @@ public enum GroupLeavingJob: JobExecutor {
let destination: Message.Destination = .closedGroup(groupPublicKey: threadId)
dependencies.storage
dependencies[singleton: .storage]
.writePublisher { db in
guard (try? SessionThread.exists(db, id: threadId)) == true else {
SNLog("[GroupLeavingJob] Failed due to non-existent group conversation")
@ -72,7 +72,7 @@ public enum GroupLeavingJob: JobExecutor {
]
// Handle the appropriate response
dependencies.storage.writeAsync { db in
dependencies[singleton: .storage].writeAsync { db in
// If it failed due to one of these errors then clear out any associated data (as somehow
// the 'SessionThread' exists but not the data required to send the 'MEMBER_LEFT' message
// which would leave the user in a state where they can't leave the group)

View File

@ -51,7 +51,7 @@ public enum MessageReceiveJob: JobExecutor {
}
}
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
for (messageInfo, protoContent) in messageData {
do {
try MessageReceiver.handle(

View File

@ -52,7 +52,7 @@ public enum MessageSendJob: JobExecutor {
// Retrieve the current attachment state
typealias AttachmentState = (error: Error?, pendingUploadAttachmentIds: [String], preparedFileIds: [String])
let attachmentState: AttachmentState = dependencies.storage
let attachmentState: AttachmentState = dependencies[singleton: .storage]
.read { db in
// If the original interaction no longer exists then don't bother sending the message (ie. the
// message was deleted before it even got sent)
@ -113,11 +113,11 @@ public enum MessageSendJob: JobExecutor {
/// If we have any pending (or failed) attachment uploads then we should create jobs for them and insert them into the
/// queue before the current job and defer it (this will mean the current job will re-run after these inserted jobs complete)
guard attachmentState.pendingUploadAttachmentIds.isEmpty else {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try attachmentState.pendingUploadAttachmentIds
.filter { attachmentId in
// Don't add a new job if there is one already in the queue
!dependencies.jobRunner.hasJob(
!dependencies[singleton: .jobRunner].hasJob(
of: .attachmentUpload,
with: AttachmentUploadJob.Details(
messageSendJobId: jobId,
@ -126,7 +126,7 @@ public enum MessageSendJob: JobExecutor {
)
}
.compactMap { attachmentId -> (jobId: Int64, job: Job)? in
dependencies.jobRunner
dependencies[singleton: .jobRunner]
.insert(
db,
job: Job(
@ -167,7 +167,7 @@ public enum MessageSendJob: JobExecutor {
///
/// **Note:** No need to upload attachments as part of this process as the above logic splits that out into it's own job
/// so we shouldn't get here until attachments have already been uploaded
dependencies.storage
dependencies[singleton: .storage]
.writePublisher { db in
try MessageSender.preparedSendData(
db,
@ -207,7 +207,7 @@ public enum MessageSendJob: JobExecutor {
if details.message is VisibleMessage {
guard
let interactionId: Int64 = job.interactionId,
dependencies.storage.read({ db in try Interaction.exists(db, id: interactionId) }) == true
dependencies[singleton: .storage].read({ db in try Interaction.exists(db, id: interactionId) }) == true
else {
// The message has been deleted so permanently fail the job
return failure(job, error, true, dependencies)

View File

@ -27,7 +27,7 @@ public enum NotifyPushServerJob: JobExecutor {
return failure(job, JobRunnerError.missingRequiredDetails, true, dependencies)
}
dependencies.storage
dependencies[singleton: .storage]
.readPublisher(using: dependencies) { db in
try PushNotificationAPI.preparedLegacyNotify(
recipient: details.message.recipient,

View File

@ -27,7 +27,7 @@ public enum RetrieveDefaultOpenGroupRoomsJob: JobExecutor {
// in the database so we need to create a dummy one to retrieve the default room data
let defaultGroupId: String = OpenGroup.idFor(roomToken: "", server: OpenGroupAPI.defaultServer)
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
guard try OpenGroup.exists(db, id: defaultGroupId) == false else { return }
_ = try OpenGroup(

View File

@ -34,7 +34,7 @@ public enum SendReadReceiptsJob: JobExecutor {
return success(job, true, dependencies)
}
dependencies.storage
dependencies[singleton: .storage]
.writePublisher { db in
try MessageSender.preparedSendData(
db,
@ -61,7 +61,7 @@ public enum SendReadReceiptsJob: JobExecutor {
var shouldFinishCurrentJob: Bool = false
let nextRunTimestamp: TimeInterval = (dependencies.dateNow.timeIntervalSince1970 + maxRunFrequency)
let updatedJob: Job? = Storage.shared.write { db in
let updatedJob: Job? = dependencies[singleton: .storage].write { db in
// If another 'sendReadReceipts' job was scheduled then update that one
// to run at 'nextRunTimestamp' and make the current job stop
if
@ -70,7 +70,7 @@ public enum SendReadReceiptsJob: JobExecutor {
.filter(Job.Columns.variant == Job.Variant.sendReadReceipts)
.filter(Job.Columns.threadId == threadId)
.fetchOne(db),
!JobRunner.isCurrentlyRunning(existingJob)
!dependencies[singleton: .jobRunner].isCurrentlyRunning(existingJob)
{
_ = try existingJob
.with(nextRunTimestamp: nextRunTimestamp)
@ -110,7 +110,12 @@ public extension SendReadReceiptsJob {
///
/// **Note:** This method assumes that the provided `interactionIds` are valid and won't filter out any invalid ids so
/// ensure that is done correctly beforehand
@discardableResult static func createOrUpdateIfNeeded(_ db: Database, threadId: String, interactionIds: [Int64]) -> Job? {
@discardableResult static func createOrUpdateIfNeeded(
_ db: Database,
threadId: String,
interactionIds: [Int64],
using dependencies: Dependencies
) -> Job? {
guard db[.areReadReceiptsEnabled] == true else { return nil }
guard !interactionIds.isEmpty else { return nil }
@ -132,7 +137,7 @@ public extension SendReadReceiptsJob {
.filter(Job.Columns.variant == Job.Variant.sendReadReceipts)
.filter(Job.Columns.threadId == threadId)
.fetchOne(db),
!JobRunner.isCurrentlyRunning(existingJob),
!dependencies[singleton: .jobRunner].isCurrentlyRunning(existingJob),
let existingDetailsData: Data = existingJob.details,
let existingDetails: Details = try? JSONDecoder().decode(Details.self, from: existingDetailsData)
{

View File

@ -24,13 +24,13 @@ public enum UpdateProfilePictureJob: JobExecutor {
// Only re-upload the profile picture if enough time has passed since the last upload
guard
let lastProfilePictureUpload: Date = dependencies.standardUserDefaults[.lastProfilePictureUpload],
let lastProfilePictureUpload: Date = dependencies[singleton: .standardUserDefaults][.lastProfilePictureUpload],
dependencies.dateNow.timeIntervalSince(lastProfilePictureUpload) > (14 * 24 * 60 * 60)
else {
// Reset the `nextRunTimestamp` value just in case the last run failed so we don't get stuck
// in a loop endlessly deferring the job
if let jobId: Int64 = job.id {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
try Job
.filter(id: jobId)
.updateAll(db, Job.Columns.nextRunTimestamp.set(to: 0))

View File

@ -50,8 +50,7 @@ public extension Message {
return .contact(publicKey: threadId)
case .legacyGroup, .group:
return .closedGroup(groupPublicKey: threadId)
case .legacyGroup, .group: return .closedGroup(groupPublicKey: threadId)
case .community:
guard let openGroup: OpenGroup = try OpenGroup.fetchOne(db, id: threadId) else {

View File

@ -59,11 +59,11 @@ public extension Crypto.Action {
id: "encryptAeadXChaCha20",
args: [message, secretKey, nonce, additionalData]
) {
guard secretKey.count == dependencies.crypto.size(.aeadXChaCha20KeyBytes) else { return nil }
guard secretKey.count == dependencies[singleton: .crypto].size(.aeadXChaCha20KeyBytes) else { return nil }
var authenticatedCipherText = Bytes(
repeating: 0,
count: message.count + dependencies.crypto.size(.aeadXChaCha20ABytes)
count: message.count + dependencies[singleton: .crypto].size(.aeadXChaCha20ABytes)
)
var authenticatedCipherTextLen: UInt64 = 0
@ -132,7 +132,7 @@ public extension Crypto.Action {
guard
!serverPubKeyData.isEmpty,
let serverPublicKeyHashBytes: Bytes = try? dependencies.crypto.perform(
let serverPublicKeyHashBytes: Bytes = try? dependencies[singleton: .crypto].perform(
.hash(message: [UInt8](serverPubKeyData), outputLength: 64)
)
else { return nil }
@ -290,11 +290,11 @@ public extension Crypto.Action {
args: [secretKey, otherBlindedPublicKey, kA, kB]
) {
let aBytes: Bytes = generatePrivateKeyScalar(secretKey: secretKey)
let combinedKeyBytes: Bytes = try dependencies.crypto.perform(
let combinedKeyBytes: Bytes = try dependencies[singleton: .crypto].perform(
.combineKeys(lhsKeyBytes: aBytes, rhsKeyBytes: otherBlindedPublicKey)
)
return try dependencies.crypto.perform(
return try dependencies[singleton: .crypto].perform(
.hash(message: (combinedKeyBytes + kA + kB), outputLength: 32)
)
}
@ -315,7 +315,7 @@ public extension Crypto.KeyPairType {
guard
edKeyPair.publicKey.count == Crypto.Action.publicKeyLength,
edKeyPair.secretKey.count == Crypto.Action.secretKeyLength,
let kBytes: Bytes = try? dependencies.crypto.perform(
let kBytes: Bytes = try? dependencies[singleton: .crypto].perform(
.generateBlindingFactor(serverPublicKey: serverPublicKey, using: dependencies)
)
else { return nil }
@ -371,7 +371,7 @@ public extension Crypto.Verification {
blindedId.prefix == .blinded15 ||
blindedId.prefix == .blinded25
),
let kBytes: Bytes = try? dependencies.crypto.perform(
let kBytes: Bytes = try? dependencies[singleton: .crypto].perform(
.generateBlindingFactor(serverPublicKey: serverPublicKey, using: dependencies)
)
else { return false }
@ -385,7 +385,7 @@ public extension Crypto.Verification {
/// Blind the positive public key
guard
let pk1: Bytes = try? dependencies.crypto.perform(
let pk1: Bytes = try? dependencies[singleton: .crypto].perform(
.combineKeys(lhsKeyBytes: kBytes, rhsKeyBytes: xEd25519Key.bytes)
)
else { return false }

View File

@ -79,7 +79,7 @@ extension OpenGroupAPI.Message {
switch SessionId.Prefix(from: sender) {
case .blinded15, .blinded25:
guard
dependencies.crypto.verify(
dependencies[singleton: .crypto].verify(
.signature(message: data.bytes, publicKey: publicKey.bytes, signature: signature.bytes)
)
else {
@ -89,7 +89,7 @@ extension OpenGroupAPI.Message {
case .standard, .unblinded:
guard
dependencies.crypto.verify(
dependencies[singleton: .crypto].verify(
.signatureEd25519(signature, publicKey: publicKey, data: data)
)
else {

View File

@ -1300,10 +1300,10 @@ public enum OpenGroupAPI {
// If we have no capabilities or if the server supports blinded keys then sign using the blinded key
if forceBlinded || capabilities.isEmpty || capabilities.contains(.blind) {
guard
let blindedKeyPair: KeyPair = dependencies.crypto.generate(
let blindedKeyPair: KeyPair = dependencies[singleton: .crypto].generate(
.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, using: dependencies)
),
let signatureResult: Bytes = try? dependencies.crypto.perform(
let signatureResult: Bytes = try? dependencies[singleton: .crypto].perform(
.sogsSignature(message: messageBytes, secretKey: userEdKeyPair.secretKey, blindedSecretKey: blindedKeyPair.secretKey, blindedPublicKey: blindedKeyPair.publicKey)
)
else { throw OpenGroupAPIError.signingFailed }
@ -1318,7 +1318,7 @@ public enum OpenGroupAPI {
switch signingType {
case .unblinded:
guard
let signatureResult: Bytes = try? dependencies.crypto.perform(
let signatureResult: Bytes = try? dependencies[singleton: .crypto].perform(
.signature(message: messageBytes, secretKey: userEdKeyPair.secretKey)
)
else { throw OpenGroupAPIError.signingFailed }
@ -1332,7 +1332,7 @@ public enum OpenGroupAPI {
default:
guard
let userKeyPair: KeyPair = Identity.fetchUserKeyPair(db),
let signatureResult: Bytes = try? dependencies.crypto.perform(
let signatureResult: Bytes = try? dependencies[singleton: .crypto].perform(
.signEd25519(data: messageBytes, keyPair: userKeyPair)
)
else { throw OpenGroupAPIError.signingFailed }
@ -1368,7 +1368,7 @@ public enum OpenGroupAPI {
guard
!serverPublicKeyData.isEmpty,
let nonce: Data = (try? dependencies.crypto.perform(.generateNonce16())).map({ Data($0) }),
let nonce: Data = (try? dependencies[singleton: .crypto].perform(.generateNonce16())).map({ Data($0) }),
let timestampBytes: Bytes = "\(timestamp)".data(using: .ascii)?.bytes
else { throw OpenGroupAPIError.signingFailed }
@ -1376,7 +1376,7 @@ public enum OpenGroupAPI {
let bodyHash: Bytes? = {
guard let body: Data = preparedRequest.request.httpBody else { return nil }
return try? dependencies.crypto.perform(.hash(message: body.bytes, outputLength: 64))
return try? dependencies[singleton: .crypto].perform(.hash(message: body.bytes, outputLength: 64))
}()
/// Generate the signature message

View File

@ -22,9 +22,9 @@ public final class OpenGroupManager {
// Run on the 'workQueue' to ensure any 'Atomic' access doesn't block the main thread
// on startup
OpenGroupAPI.workQueue.async(using: dependencies) {
guard !dependencies.caches[.openGroupManager].isPolling else { return }
guard !dependencies[cache: .openGroupManager].isPolling else { return }
let servers: Set<String> = dependencies.storage
let servers: Set<String> = dependencies[singleton: .storage]
.read { db in
// The default room promise creates an OpenGroup with an empty `roomToken` value,
// we don't want to start a poller for this as the user hasn't actually joined a room
@ -39,7 +39,7 @@ public final class OpenGroupManager {
.defaulting(to: [])
// Update the cache state and re-create all of the pollers
dependencies.caches.mutate(cache: .openGroupManager) { cache in
dependencies.mutate(cache: .openGroupManager) { cache in
cache.isPolling = true
cache.pollers = servers
.reduce(into: [:]) { result, server in
@ -49,13 +49,13 @@ public final class OpenGroupManager {
}
// Now that the pollers have been created actually start them
dependencies.caches[.openGroupManager].pollers
dependencies[cache: .openGroupManager].pollers
.forEach { _, poller in poller.startIfNeeded(using: dependencies) }
}
}
public func stopPolling(using dependencies: Dependencies = Dependencies()) {
dependencies.caches.mutate(cache: .openGroupManager) {
dependencies.mutate(cache: .openGroupManager) {
$0.pollers.forEach { _, openGroupPoller in openGroupPoller.stop() }
$0.pollers.removeAll()
$0.isPolling = false
@ -140,7 +140,7 @@ public final class OpenGroupManager {
}
// First check if there is no poller for the specified server
if Set(dependencies.caches[.openGroupManager].pollers.keys).intersection(serverOptions).isEmpty {
if Set(dependencies[cache: .openGroupManager].pollers.keys).intersection(serverOptions).isEmpty {
return false
}
@ -250,7 +250,7 @@ public final class OpenGroupManager {
return OpenGroupAPI.defaultServer
}()
return dependencies.storage
return dependencies[singleton: .storage]
.readPublisher { db in
try OpenGroupAPI
.preparedCapabilitiesAndRoom(
@ -263,7 +263,7 @@ public final class OpenGroupManager {
.flatMap { $0.send(using: dependencies) }
.flatMap { info, response -> Future<Void, Error> in
Future<Void, Error> { resolver in
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
// Add the new open group to libSession
if !calledFromConfigHandling {
try SessionUtil.add(
@ -336,9 +336,9 @@ public final class OpenGroupManager {
.defaulting(to: 1)
if numActiveRooms == 1, let server: String = server?.lowercased() {
let poller = dependencies.caches[.openGroupManager].pollers[server]
let poller = dependencies[cache: .openGroupManager].pollers[server]
poller?.stop()
dependencies.caches.mutate(cache: .openGroupManager) { $0.pollers[server] = nil }
dependencies.mutate(cache: .openGroupManager) { $0.pollers[server] = nil }
}
// Remove all the data (everything should cascade delete)
@ -504,13 +504,13 @@ public final class OpenGroupManager {
// above transaction
OpenGroupAPI.workQueue.async(using: dependencies) {
// Start the poller if needed
if dependencies.caches[.openGroupManager].pollers[server.lowercased()] == nil {
dependencies.caches.mutate(cache: .openGroupManager) {
if dependencies[cache: .openGroupManager].pollers[server.lowercased()] == nil {
dependencies.mutate(cache: .openGroupManager) {
$0.pollers[server.lowercased()]?.stop()
$0.pollers[server.lowercased()] = OpenGroupAPI.Poller(for: server.lowercased())
}
dependencies.caches[.openGroupManager].pollers[server.lowercased()]?
dependencies[cache: .openGroupManager].pollers[server.lowercased()]?
.startIfNeeded(using: dependencies)
}
@ -541,7 +541,7 @@ public final class OpenGroupManager {
}
},
receiveValue: { data in
dependencies.storage.write(using: dependencies) { db in
dependencies[singleton: .storage].write(using: dependencies) { db in
_ = try OpenGroup
.filter(id: threadId)
.updateAll(db, OpenGroup.Columns.imageData.set(to: data))
@ -640,7 +640,7 @@ public final class OpenGroupManager {
db,
openGroupId: openGroup.id,
message: message,
associatedPendingChanges: dependencies.caches[.openGroupManager].pendingChanges
associatedPendingChanges: dependencies[cache: .openGroupManager].pendingChanges
.filter {
guard $0.server == server && $0.room == roomToken && $0.changeType == .reaction else {
return false
@ -687,7 +687,7 @@ public final class OpenGroupManager {
.updateAll(db, OpenGroup.Columns.sequenceNumber.set(to: largestValidSeqNo))
// Update pendingChange cache based on the `largestValidSeqNo` value
dependencies.caches.mutate(cache: .openGroupManager) {
dependencies.mutate(cache: .openGroupManager) {
$0.pendingChanges = $0.pendingChanges
.filter { $0.seqNo == nil || $0.seqNo! > largestValidSeqNo }
}
@ -838,7 +838,7 @@ public final class OpenGroupManager {
)
)
dependencies.caches.mutate(cache: .openGroupManager) {
dependencies.mutate(cache: .openGroupManager) {
$0.pendingChanges.append(pendingChange)
}
@ -850,7 +850,7 @@ public final class OpenGroupManager {
seqNo: Int64?,
using dependencies: Dependencies = Dependencies()
) {
dependencies.caches.mutate(cache: .openGroupManager) {
dependencies.mutate(cache: .openGroupManager) {
if let index = $0.pendingChanges.firstIndex(of: pendingChange) {
$0.pendingChanges[index].seqNo = seqNo
}
@ -861,7 +861,7 @@ public final class OpenGroupManager {
_ pendingChange: OpenGroupAPI.PendingChange,
using dependencies: Dependencies = Dependencies()
) {
dependencies.caches.mutate(cache: .openGroupManager) {
dependencies.mutate(cache: .openGroupManager) {
if let index = $0.pendingChanges.firstIndex(of: pendingChange) {
$0.pendingChanges.remove(at: index)
}
@ -877,7 +877,7 @@ public final class OpenGroupManager {
) -> Bool {
guard let server: String = server else { return false }
guard let db: Database = db else {
return dependencies.storage
return dependencies[singleton: .storage]
.read { db in doesOpenGroupSupport(db, capability: capability, on: server, using: dependencies) }
.defaulting(to: false)
}
@ -905,7 +905,7 @@ public final class OpenGroupManager {
let groupId: String = OpenGroup.idFor(roomToken: roomToken, server: server)
let targetRoles: [GroupMember.Role] = [.moderator, .admin]
return dependencies.storage
return dependencies[singleton: .storage]
.read { db -> Bool in
let isDirectModOrAdmin: Bool = GroupMember
.filter(GroupMember.Columns.groupId == groupId)
@ -946,7 +946,7 @@ public final class OpenGroupManager {
.filter(id: groupId)
.asRequest(of: String.self)
.fetchOne(db),
let blindedKeyPair: KeyPair = dependencies.crypto.generate(
let blindedKeyPair: KeyPair = dependencies[singleton: .crypto].generate(
.blindedKeyPair(
serverPublicKey: openGroupPublicKey,
edKeyPair: userEdKeyPair,
@ -989,12 +989,12 @@ public final class OpenGroupManager {
using dependencies: Dependencies = Dependencies()
) -> AnyPublisher<[DefaultRoomInfo], Error> {
// Note: If we already have a 'defaultRoomsPromise' then there is no need to get it again
if let existingPublisher: AnyPublisher<[DefaultRoomInfo], Error> = dependencies.caches[.openGroupManager].defaultRoomsPublisher {
if let existingPublisher: AnyPublisher<[DefaultRoomInfo], Error> = dependencies[cache: .openGroupManager].defaultRoomsPublisher {
return existingPublisher
}
// Try to retrieve the default rooms 8 times
let publisher: AnyPublisher<[DefaultRoomInfo], Error> = dependencies.storage
let publisher: AnyPublisher<[DefaultRoomInfo], Error> = dependencies[singleton: .storage]
.readPublisher { db -> HTTP.PreparedRequest<OpenGroupAPI.CapabilitiesAndRoomsResponse> in
try OpenGroupAPI.preparedCapabilitiesAndRooms(
db,
@ -1007,7 +1007,7 @@ public final class OpenGroupManager {
.receive(on: OpenGroupAPI.workQueue, using: dependencies)
.retry(8, using: dependencies)
.map { info, response -> [DefaultRoomInfo]? in
dependencies.storage.write { db -> [DefaultRoomInfo] in
dependencies[singleton: .storage].write { db -> [DefaultRoomInfo] in
// Store the capabilities first
OpenGroupManager.handleCapabilities(
db,
@ -1070,7 +1070,7 @@ public final class OpenGroupManager {
switch result {
case .finished: break
case .failure:
dependencies.caches.mutate(cache: .openGroupManager) { cache in
dependencies.mutate(cache: .openGroupManager) { cache in
cache.defaultRoomsPublisher = nil
}
}
@ -1079,7 +1079,7 @@ public final class OpenGroupManager {
.shareReplay(1)
.eraseToAnyPublisher()
dependencies.caches.mutate(cache: .openGroupManager) { cache in
dependencies.mutate(cache: .openGroupManager) { cache in
cache.defaultRoomsPublisher = publisher
}
@ -1106,7 +1106,7 @@ public final class OpenGroupManager {
// don't double up on fetch requests by storing the existing request as a promise if
// there is one.
let threadId: String = OpenGroup.idFor(roomToken: roomToken, server: server)
let lastOpenGroupImageUpdate: Date? = dependencies.standardUserDefaults[.lastOpenGroupImageUpdate]
let lastOpenGroupImageUpdate: Date? = dependencies[singleton: .standardUserDefaults][.lastOpenGroupImageUpdate]
let now: Date = dependencies.dateNow
let timeSinceLastUpdate: TimeInterval = (lastOpenGroupImageUpdate.map { now.timeIntervalSince($0) } ?? .greatestFiniteMagnitude)
let updateInterval: TimeInterval = (7 * 24 * 60 * 60)
@ -1121,7 +1121,7 @@ public final class OpenGroupManager {
.eraseToAnyPublisher()
}
if let publisher: AnyPublisher<Data, Error> = dependencies.caches[.openGroupManager].groupImagePublishers[threadId] {
if let publisher: AnyPublisher<Data, Error> = dependencies[cache: .openGroupManager].groupImagePublishers[threadId] {
return publisher
}
@ -1130,7 +1130,7 @@ public final class OpenGroupManager {
Future { resolver in
DispatchQueue.global(qos: .background).async(using: dependencies) {
// Hold on to the publisher until it has completed at least once
dependencies.storage
dependencies[singleton: .storage]
.readPublisher { db -> (Data?, HTTP.PreparedRequest<Data>?) in
if canUseExistingImage {
let maybeExistingData: Data? = try? OpenGroup
@ -1182,12 +1182,12 @@ public final class OpenGroupManager {
},
receiveValue: { imageData in
if server.lowercased() == OpenGroupAPI.defaultServer {
dependencies.storage.write { db in
dependencies[singleton: .storage].write { db in
_ = try OpenGroup
.filter(id: threadId)
.updateAll(db, OpenGroup.Columns.imageData.set(to: imageData))
}
dependencies.standardUserDefaults[.lastOpenGroupImageUpdate] = now
dependencies[singleton: .standardUserDefaults][.lastOpenGroupImageUpdate] = now
}
resolver(Result.success(imageData))
@ -1205,7 +1205,7 @@ public final class OpenGroupManager {
.subscribe(on: DispatchQueue.global(qos: .background), using: dependencies)
.sinkUntilComplete()
dependencies.caches.mutate(cache: .openGroupManager) { cache in
dependencies.mutate(cache: .openGroupManager) { cache in
cache.groupImagePublishers[threadId] = publisher
}
@ -1233,7 +1233,7 @@ public extension OpenGroupManager {
return storedTimeSinceLastOpen
}
guard let lastOpen: Date = dependencies.standardUserDefaults[.lastOpen] else {
guard let lastOpen: Date = dependencies[singleton: .standardUserDefaults][.lastOpen] else {
_timeSinceLastOpen = .greatestFiniteMagnitude
return .greatestFiniteMagnitude
}
@ -1248,7 +1248,7 @@ public extension OpenGroupManager {
public extension Cache {
static let openGroupManager: CacheInfo.Config<OGMCacheType, OGMImmutableCacheType> = CacheInfo.create(
createInstance: { OpenGroupManager.Cache() },
createInstance: { _ in OpenGroupManager.Cache() },
mutableInstance: { $0 },
immutableInstance: { $0 }
)

Some files were not shown because too many files have changed in this diff Show More