Various dependency changes required to get unit tests working correctly

• Updated the SessionCallManager to be an updated singleton type (cleaned up more in Groups Rebuild)
• Updated the PushRegistrationManager to be an updated singleton type (cleaned up more in Groups Rebuild)
• Injected dependencies correctly in a bunch of places
pull/1053/head
Morgan Pretty 5 months ago
parent 2ee1fa0125
commit e1c5215986

@ -685,6 +685,8 @@
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 */; };
FD6C67242CF6E72E00B350A7 /* NoopSessionCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6C67232CF6E72900B350A7 /* NoopSessionCallManager.swift */; };
FD6C67262CF6EA2300B350A7 /* PushRegistrationManagerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6C67252CF6EA1E00B350A7 /* PushRegistrationManagerType.swift */; };
FD6DF00B2ACFE40D0084BA4C /* _005_AddSnodeReveivedMessageInfoPrimaryKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6DF00A2ACFE40D0084BA4C /* _005_AddSnodeReveivedMessageInfoPrimaryKey.swift */; };
FD6E4C8A2A1AEE4700C7C243 /* LegacyUnsubscribeRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6E4C892A1AEE4700C7C243 /* LegacyUnsubscribeRequest.swift */; };
FD705A92278D051200F16121 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A91278D051200F16121 /* ReusableView.swift */; };
@ -1832,6 +1834,8 @@
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>"; };
FD6C67232CF6E72900B350A7 /* NoopSessionCallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoopSessionCallManager.swift; sourceTree = "<group>"; };
FD6C67252CF6EA1E00B350A7 /* PushRegistrationManagerType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushRegistrationManagerType.swift; sourceTree = "<group>"; };
FD6DF00A2ACFE40D0084BA4C /* _005_AddSnodeReveivedMessageInfoPrimaryKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _005_AddSnodeReveivedMessageInfoPrimaryKey.swift; sourceTree = "<group>"; };
FD6E4C892A1AEE4700C7C243 /* LegacyUnsubscribeRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyUnsubscribeRequest.swift; sourceTree = "<group>"; };
FD705A91278D051200F16121 /* ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
@ -2783,6 +2787,7 @@
FD716E692850327900C96BF4 /* EndCallMode.swift */,
FD716E6528502EE200C96BF4 /* CurrentCallProtocol.swift */,
FD716E6328502DDD00C96BF4 /* CallManagerProtocol.swift */,
FD6C67232CF6E72900B350A7 /* NoopSessionCallManager.swift */,
);
path = Calls;
sourceTree = "<group>";
@ -3177,6 +3182,7 @@
children = (
FDC498B52AC15F6D00EDD897 /* Types */,
4539B5851F79348F007141FF /* PushRegistrationManager.swift */,
FD6C67252CF6EA1E00B350A7 /* PushRegistrationManagerType.swift */,
45CD81EE1DC030E7004C9430 /* SyncPushTokensJob.swift */,
451A13B01E13DED2000A50FD /* AppNotifications.swift */,
450DF2081E0DD2C6003D14BE /* UserNotificationsAdaptee.swift */,
@ -5929,6 +5935,7 @@
FDC438A427BB107F00C60D73 /* UserBanRequest.swift in Sources */,
FD4C53AF2CC1D62E003B10F4 /* _021_ReworkRecipientState.swift in Sources */,
C379DCF4256735770002D4EB /* VisibleMessage+Attachment.swift in Sources */,
FD6C67242CF6E72E00B350A7 /* NoopSessionCallManager.swift in Sources */,
FDB4BBC72838B91E00B7C95D /* LinkPreviewError.swift in Sources */,
FD09798327FD1A1500936362 /* ClosedGroup.swift in Sources */,
FDC13D542A16FF29007267C7 /* LegacyGroupRequest.swift in Sources */,
@ -6226,6 +6233,7 @@
FDEF57242C3CF04700131302 /* WebRTC+Utilities.swift in Sources */,
34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */,
9422568C2C23F8C800C0FDBF /* DisplayNameScreen.swift in Sources */,
FD6C67262CF6EA2300B350A7 /* PushRegistrationManagerType.swift in Sources */,
B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */,
7B9F71D72853100A006DFE7B /* Emoji+Available.swift in Sources */,
FD09C5E628260FF9000CE219 /* MediaGalleryViewModel.swift in Sources */,

@ -15,6 +15,8 @@ import SessionSnodeKit
public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
@objc static let isEnabled = true
private let dependencies: Dependencies
// MARK: - Metadata Properties
public let uuid: String
public let callId: UUID // This is for CallKit
@ -147,7 +149,8 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
// MARK: - Initialization
init(_ db: Database, for sessionId: String, uuid: String, mode: CallMode, outgoing: Bool = false) {
init(_ db: Database, for sessionId: String, uuid: String, mode: CallMode, outgoing: Bool = false, using dependencies: Dependencies) {
self.dependencies = dependencies
self.sessionId = sessionId
self.uuid = uuid
self.callId = UUID()
@ -172,8 +175,8 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
WebRTCSession.current = self.webRTCSession
self.webRTCSession.delegate = self
if AppEnvironment.shared.callManager.currentCall == nil {
AppEnvironment.shared.callManager.currentCall = self
if Singleton.callManager.currentCall == nil {
Singleton.callManager.setCurrentCall(self)
}
else {
SNLog("[Calls] A call is ongoing.")
@ -183,12 +186,12 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
// stringlint:ignore_contents
func reportIncomingCallIfNeeded(completion: @escaping (Error?) -> Void) {
guard case .answer = mode else {
SessionCallManager.reportFakeCall(info: "Call not in answer mode")
SessionCallManager.reportFakeCall(info: "Call not in answer mode", using: dependencies)
return
}
setupTimeoutTimer()
AppEnvironment.shared.callManager.reportIncomingCall(self, callerName: contactName) { error in
Singleton.callManager.reportIncomingCall(self, callerName: contactName) { error in
completion(error)
}
}
@ -290,7 +293,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
// MARK: - Call Message Handling
public func updateCallMessage(mode: EndCallMode) {
public func updateCallMessage(mode: EndCallMode, using dependencies: Dependencies) {
guard let callInteractionId: Int64 = callInteractionId else { return }
let duration: TimeInterval = self.duration
@ -351,11 +354,12 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
threadId: interaction.threadId,
threadVariant: threadVariant,
includingOlder: false,
trySendReadReceipt: false
trySendReadReceipt: false,
using: dependencies
)
},
completion: { _, _ in
SessionCallManager.suspendDatabaseIfCallEndedInBackground()
Singleton.callManager.suspendDatabaseIfCallEndedInBackground()
}
)
}
@ -404,7 +408,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
guard Singleton.hasAppContext else { return }
if let callVC = Singleton.appContext.frontmostViewController as? CallVC { callVC.handleEndCallMessage() }
if let miniCallView = MiniCallView.current { miniCallView.dismiss() }
AppEnvironment.shared.callManager.reportCurrentCallEnded(reason: .remoteEnded)
Singleton.callManager.reportCurrentCallEnded(reason: .remoteEnded)
}
}
@ -461,7 +465,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
timeOutTimer = Timer.scheduledTimerOnMainThread(withTimeInterval: timeInterval, repeats: false) { _ in
self.didTimeout = true
AppEnvironment.shared.callManager.endCall(self) { error in
Singleton.callManager.endCall(self) { error in
self.timeOutTimer = nil
}
}

@ -2,12 +2,16 @@
import Foundation
import CallKit
import SessionMessagingKit
import SessionUtilitiesKit
extension SessionCallManager {
public func startCall(_ call: SessionCall, completion: ((Error?) -> Void)?) {
guard case .offer = call.mode else { return }
guard !call.hasConnected else { return }
public func startCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?) {
guard
let call: SessionCall = call as? SessionCall,
case .offer = call.mode,
!call.hasConnected
else { return }
reportOutgoingCall(call)
@ -28,9 +32,9 @@ extension SessionCallManager {
}
}
public func answerCall(_ call: SessionCall, completion: ((Error?) -> Void)?) {
if callController != nil {
let answerCallAction = CXAnswerCallAction(call: call.callId)
public func answerCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?) {
if callController != nil, let callId: UUID = call?.callId {
let answerCallAction = CXAnswerCallAction(call: callId)
let transaction = CXTransaction()
transaction.addAction(answerCallAction)
@ -42,9 +46,9 @@ extension SessionCallManager {
}
}
public func endCall(_ call: SessionCall, completion: ((Error?) -> Void)?) {
if callController != nil {
let endCallAction = CXEndCallAction(call: call.callId)
public func endCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?) {
if callController != nil, let callId: UUID = call?.callId {
let endCallAction = CXEndCallAction(call: callId)
let transaction = CXTransaction()
transaction.addAction(endCallAction)

@ -9,7 +9,21 @@ import SessionMessagingKit
import SignalUtilitiesKit
import SessionUtilitiesKit
// MARK: - Cache
public extension Cache {
static let callManager: CacheInfo.Config<CallManagerCacheType, CallManagerImmutableCacheType> = CacheInfo.create(
createInstance: { SessionCallManager.Cache() },
mutableInstance: { $0 },
immutableInstance: { $0 }
)
}
// MARK: - SessionCallManager
public final class SessionCallManager: NSObject, CallManagerProtocol {
let dependencies: Dependencies
let provider: CXProvider?
let callController: CXCallController?
@ -27,40 +41,15 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
}
}
private static var _sharedProvider: CXProvider?
static func sharedProvider(useSystemCallLog: Bool) -> CXProvider {
let configuration = buildProviderConfiguration(useSystemCallLog: useSystemCallLog)
if let sharedProvider = self._sharedProvider {
sharedProvider.configuration = configuration
return sharedProvider
}
else {
SwiftSingletons.register(self)
let provider = CXProvider(configuration: configuration)
_sharedProvider = provider
return provider
}
}
static func buildProviderConfiguration(useSystemCallLog: Bool) -> CXProviderConfiguration {
let providerConfiguration = CXProviderConfiguration()
providerConfiguration.supportsVideo = true
providerConfiguration.maximumCallGroups = 1
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.supportedHandleTypes = [.generic]
let iconMaskImage = #imageLiteral(resourceName: "SessionGreen32")
providerConfiguration.iconTemplateImageData = iconMaskImage.pngData()
providerConfiguration.includesCallsInRecents = useSystemCallLog
return providerConfiguration
}
// MARK: - Initialization
init(useSystemCallLog: Bool = false) {
init(useSystemCallLog: Bool = false, using dependencies: Dependencies) {
self.dependencies = dependencies
if Preferences.isCallKitSupported {
self.provider = SessionCallManager.sharedProvider(useSystemCallLog: useSystemCallLog)
self.provider = dependencies.caches.mutate(cache: .callManager) {
$0.getOrCreateProvider(useSystemCallLog: useSystemCallLog)
}
self.callController = CXCallController()
}
else {
@ -76,9 +65,11 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
// MARK: - Report calls
public static func reportFakeCall(info: String) {
public static func reportFakeCall(info: String, using dependencies: Dependencies) {
let callId = UUID()
let provider = SessionCallManager.sharedProvider(useSystemCallLog: false)
let provider: CXProvider = dependencies.caches.mutate(cache: .callManager) {
$0.getOrCreateProvider(useSystemCallLog: false)
}
provider.reportNewIncomingCall(
with: callId,
update: CXCallUpdate()
@ -92,6 +83,10 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
)
}
public func setCurrentCall(_ call: CurrentCallProtocol?) {
self.currentCall = call
}
public func reportOutgoingCall(_ call: SessionCall) {
Log.assertOnMainThread()
UserDefaults.sharedLokiProject?[.isCallOngoing] = true
@ -108,8 +103,10 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
}
}
public func reportIncomingCall(_ call: SessionCall, callerName: String, completion: @escaping (Error?) -> Void) {
let provider = provider ?? Self.sharedProvider(useSystemCallLog: false)
public func reportIncomingCall(_ call: CurrentCallProtocol, callerName: String, completion: @escaping (Error?) -> Void) {
let provider: CXProvider = dependencies.caches.mutate(cache: .callManager) {
$0.getOrCreateProvider(useSystemCallLog: false)
}
// Construct a CXCallUpdate describing the incoming call, including the caller.
let update = CXCallUpdate()
update.localizedCallerName = callerName
@ -152,7 +149,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
guard let call = currentCall else {
handleCallEnded()
Self.suspendDatabaseIfCallEndedInBackground()
suspendDatabaseIfCallEndedInBackground()
return
}
@ -160,14 +157,14 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
self.provider?.reportCall(with: call.callId, endedAt: nil, reason: reason)
switch (reason) {
case .answeredElsewhere: call.updateCallMessage(mode: .answeredElsewhere)
case .unanswered: call.updateCallMessage(mode: .unanswered)
case .declinedElsewhere: call.updateCallMessage(mode: .local)
default: call.updateCallMessage(mode: .remote)
case .answeredElsewhere: call.updateCallMessage(mode: .answeredElsewhere, using: dependencies)
case .unanswered: call.updateCallMessage(mode: .unanswered, using: dependencies)
case .declinedElsewhere: call.updateCallMessage(mode: .local, using: dependencies)
default: call.updateCallMessage(mode: .remote, using: dependencies)
}
}
else {
call.updateCallMessage(mode: .local)
call.updateCallMessage(mode: .local, using: dependencies)
}
(call as? SessionCall)?.webRTCSession.dropConnection()
@ -197,7 +194,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
callUpdate.supportsDTMF = false
}
public static func suspendDatabaseIfCallEndedInBackground() {
public func suspendDatabaseIfCallEndedInBackground() {
if Singleton.hasAppContext && Singleton.appContext.isInBackground {
// FIXME: Initialise the `SessionCallManager` with a dependencies instance
let dependencies: Dependencies = Dependencies()
@ -214,9 +211,11 @@ 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 {
return
}
guard
let call: SessionCall = Storage.shared.read({ [dependencies] db in
SessionCall(db, for: caller, uuid: uuid, mode: mode, using: dependencies)
})
else { return }
call.callInteractionId = interactionId
call.reportIncomingCallIfNeeded { error in
@ -295,3 +294,38 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
}
}
// MARK: - SessionCallManager Cache
public extension SessionCallManager {
class Cache: CallManagerCacheType {
public var provider: CXProvider?
public func getOrCreateProvider(useSystemCallLog: Bool) -> CXProvider {
if let provider: CXProvider = self.provider {
return provider
}
let iconMaskImage: UIImage = #imageLiteral(resourceName: "SessionGreen32")
let configuration = CXProviderConfiguration()
configuration.supportsVideo = true
configuration.maximumCallGroups = 1
configuration.maximumCallsPerCallGroup = 1
configuration.supportedHandleTypes = [.generic]
configuration.iconTemplateImageData = iconMaskImage.pngData()
configuration.includesCallsInRecents = useSystemCallLog
let provider: CXProvider = CXProvider(configuration: configuration)
self.provider = provider
return provider
}
}
}
// MARK: - OGMCacheType
/// This is a read-only version of the Cache designed to avoid unintentionally mutating the instance in a non-thread-safe way
public protocol CallManagerImmutableCacheType: ImmutableCacheType {}
public protocol CallManagerCacheType: CallManagerImmutableCacheType, MutableCacheType {
func getOrCreateProvider(useSystemCallLog: Bool) -> CXProvider
}

@ -429,7 +429,7 @@ final class CallVC: UIViewController, VideoPreviewDelegate {
_ = call.videoCapturer // Force the lazy var to instantiate
titleLabel.text = self.call.contactName
AppEnvironment.shared.callManager.startCall(call) { [weak self] error in
Singleton.callManager.startCall(call) { [weak self] error in
DispatchQueue.main.async {
if let _ = error {
self?.callInfoLabel.text = "callsErrorStart".localized()
@ -606,7 +606,7 @@ final class CallVC: UIViewController, VideoPreviewDelegate {
}
@objc private func answerCall() {
AppEnvironment.shared.callManager.answerCall(call) { [weak self] error in
Singleton.callManager.answerCall(call) { [weak self] error in
DispatchQueue.main.async {
if let _ = error {
self?.callInfoLabel.text = "callsErrorAnswer".localized()
@ -617,10 +617,10 @@ final class CallVC: UIViewController, VideoPreviewDelegate {
}
@objc private func endCall() {
AppEnvironment.shared.callManager.endCall(call) { [weak self] error in
Singleton.callManager.endCall(call) { [weak self] error in
if let _ = error {
self?.call.endSessionCall()
AppEnvironment.shared.callManager.reportCurrentCallEnded(reason: nil)
Singleton.callManager.reportCurrentCallEnded(reason: nil)
}
DispatchQueue.main.async {

@ -184,10 +184,10 @@ final class IncomingCallBanner: UIView, UIGestureRecognizerDelegate {
}
@objc private func endCall() {
AppEnvironment.shared.callManager.endCall(call) { error in
Singleton.callManager.endCall(call) { error in
if let _ = error {
self.call.endSessionCall()
AppEnvironment.shared.callManager.reportCurrentCallEnded(reason: nil)
Singleton.callManager.reportCurrentCallEnded(reason: nil)
}
self.dismiss()

@ -120,12 +120,21 @@ extension ConversationVC:
let threadId: String = self.viewModel.threadData.threadId
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 {
return
}
guard
AVAudioSession.sharedInstance().recordPermission == .granted,
self.viewModel.threadData.threadVariant == .contact,
Singleton.callManager.currentCall == nil,
let call: SessionCall = Storage.shared.read({ [dependencies = viewModel.dependencies] db in
SessionCall(
db,
for: threadId,
uuid: UUID().uuidString.lowercased(),
mode: .offer,
outgoing: true,
using: dependencies
)
})
else { return }
let callVC = CallVC(for: call)
callVC.conversationVC = self
@ -540,7 +549,8 @@ extension ConversationVC:
.updateAllAndConfig(
db,
SessionThread.Columns.shouldBeVisible.set(to: true),
SessionThread.Columns.pinnedPriority.set(to: LibSession.visiblePriority)
SessionThread.Columns.pinnedPriority.set(to: LibSession.visiblePriority),
using: dependencies
)
}
@ -958,7 +968,8 @@ extension ConversationVC:
.update(
db,
sessionId: cellViewModel.threadId,
disappearingMessagesConfig: messageDisappearingConfig
disappearingMessagesConfig: messageDisappearingConfig,
using: dependencies
)
}
self?.dismiss(animated: true, completion: nil)
@ -1517,7 +1528,11 @@ extension ConversationVC:
if self?.viewModel.threadData.threadShouldBeVisible == false {
_ = try SessionThread
.filter(id: cellViewModel.threadId)
.updateAllAndConfig(db, SessionThread.Columns.shouldBeVisible.set(to: true))
.updateAllAndConfig(
db,
SessionThread.Columns.shouldBeVisible.set(to: true),
using: dependencies
)
}
let pendingReaction: Reaction? = {
@ -2721,7 +2736,8 @@ extension ConversationVC {
db,
Contact.Columns.isApproved.set(to: true),
Contact.Columns.didApproveMe
.set(to: contact.didApproveMe || !isNewThread)
.set(to: contact.didApproveMe || !isNewThread),
using: dependencies
)
}
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
@ -2751,7 +2767,8 @@ extension ConversationVC {
tableView: self.tableView,
threadViewModel: self.viewModel.threadData,
viewController: self,
navigatableStateHolder: nil
navigatableStateHolder: nil,
using: viewModel.dependencies
)
guard let action: UIContextualAction = actions?.first else { return }
@ -2775,7 +2792,8 @@ extension ConversationVC {
tableView: self.tableView,
threadViewModel: self.viewModel.threadData,
viewController: self,
navigatableStateHolder: nil
navigatableStateHolder: nil,
using: viewModel.dependencies
)
guard let action: UIContextualAction = actions?.first else { return }

@ -782,18 +782,18 @@ public class ConversationViewModel: OWSAudioPlayerDelegate, NavigatableStateHold
markAsReadPublisher = markAsReadTrigger
.throttle(for: .milliseconds(100), scheduler: DispatchQueue.global(qos: .userInitiated), latest: true)
.handleEvents(
receiveOutput: { [weak self] target, timestampMs in
receiveOutput: { [weak self, dependencies] target, timestampMs in
let threadData: SessionThreadViewModel? = self?._threadData.wrappedValue
switch target {
case .thread: threadData?.markAsRead(target: target)
case .thread: threadData?.markAsRead(target: target, using: dependencies)
case .threadAndInteractions(let interactionId):
guard
timestampMs == nil ||
(self?.lastInteractionTimestampMsMarkedAsRead ?? 0) < (timestampMs ?? 0) ||
(self?.lastInteractionIdMarkedAsRead ?? 0) < (interactionId ?? 0)
else {
threadData?.markAsRead(target: .thread)
threadData?.markAsRead(target: .thread, using: dependencies)
return
}
@ -804,7 +804,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate, NavigatableStateHold
}
self?.lastInteractionIdMarkedAsRead = (interactionId ?? threadData?.interactionId)
threadData?.markAsRead(target: target)
threadData?.markAsRead(target: target, using: dependencies)
}
}
)
@ -872,10 +872,14 @@ public class ConversationViewModel: OWSAudioPlayerDelegate, NavigatableStateHold
let threadId: String = self.threadId
let displayName: String = self._threadData.wrappedValue.displayName
Storage.shared.writeAsync { db in
Storage.shared.writeAsync { [dependencies] db in
try Contact
.filter(id: threadId)
.updateAllAndConfig(db, Contact.Columns.isBlocked.set(to: false))
.updateAllAndConfig(
db,
Contact.Columns.isBlocked.set(to: false),
using: dependencies
)
}
}

@ -375,23 +375,23 @@ class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel, Naviga
}
// Contacts & legacy closed groups need to update the LibSession
dependencies.storage.writeAsync(using: dependencies) { [threadId, threadVariant] db in
dependencies.storage.writeAsync(using: dependencies) { [threadId, threadVariant, dependencies] db in
switch threadVariant {
case .contact:
try LibSession
.update(
db,
sessionId: threadId,
disappearingMessagesConfig: updatedConfig
)
try LibSession.update(
db,
sessionId: threadId,
disappearingMessagesConfig: updatedConfig,
using: dependencies
)
case .legacyGroup:
try LibSession
.update(
db,
groupPublicKey: threadId,
disappearingConfig: updatedConfig
)
try LibSession.update(
db,
groupPublicKey: threadId,
disappearingConfig: updatedConfig,
using: dependencies
)
default: break
}

@ -157,7 +157,8 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
.updateAllAndConfig(
db,
Profile.Columns.nickname
.set(to: (updatedNickname.isEmpty ? nil : editedDisplayName))
.set(to: (updatedNickname.isEmpty ? nil : editedDisplayName)),
using: dependencies
)
}
}
@ -537,7 +538,8 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
db,
type: .leaveGroupAsync,
threadId: threadViewModel.threadId,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
}
@ -837,12 +839,13 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
) {
guard oldBlockedState != isBlocked else { return }
dependencies.storage.writeAsync { db in
dependencies.storage.writeAsync { [dependencies] db in
try Contact
.filter(id: threadId)
.updateAllAndConfig(
db,
Contact.Columns.isBlocked.set(to: isBlocked)
Contact.Columns.isBlocked.set(to: isBlocked),
using: dependencies
)
}
}

@ -751,7 +751,8 @@ final class HomeVC: BaseVC, LibSessionRespondingViewController, UITableViewDataS
tableView: tableView,
threadViewModel: threadViewModel,
viewController: self,
navigatableStateHolder: viewModel
navigatableStateHolder: viewModel,
using: viewModel.dependencies
)
)
@ -773,7 +774,8 @@ final class HomeVC: BaseVC, LibSessionRespondingViewController, UITableViewDataS
tableView: tableView,
threadViewModel: threadViewModel,
viewController: self,
navigatableStateHolder: viewModel
navigatableStateHolder: viewModel,
using: viewModel.dependencies
)
)
@ -820,7 +822,8 @@ final class HomeVC: BaseVC, LibSessionRespondingViewController, UITableViewDataS
tableView: tableView,
threadViewModel: threadViewModel,
viewController: self,
navigatableStateHolder: viewModel
navigatableStateHolder: viewModel,
using: viewModel.dependencies
)
)

@ -208,7 +208,8 @@ class MessageRequestsViewModel: SessionTableViewModel, NavigatableStateHolder, O
threadIds: threadInfo
.filter { _, variant in variant == .contact }
.map { id, _ in id },
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
// Remove the group requests
@ -218,7 +219,8 @@ class MessageRequestsViewModel: SessionTableViewModel, NavigatableStateHolder, O
threadIds: threadInfo
.filter { _, variant in variant == .legacyGroup || variant == .group }
.map { id, _ in id },
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
}
@ -257,7 +259,8 @@ class MessageRequestsViewModel: SessionTableViewModel, NavigatableStateHolder, O
tableView: tableView,
threadViewModel: threadViewModel,
viewController: viewController,
navigatableStateHolder: nil
navigatableStateHolder: nil,
using: dependencies
)
)

@ -38,7 +38,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
verifyDBKeysAvailableBeforeBackgroundLaunch()
_ = AppVersion.shared
AppEnvironment.shared.pushRegistrationManager.createVoipRegistryIfNecessary()
Singleton.setPushRegistrationManager(PushRegistrationManager(using: dependencies))
Singleton.pushRegistrationManager.createVoipRegistryIfNecessary()
// Prevent the device from sleeping during database view async registration
// (e.g. long database upgrades).
@ -50,10 +51,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
self.loadingViewController = LoadingViewController()
AppSetup.setupEnvironment(
appSpecificBlock: {
appSpecificBlock: { [dependencies] in
Log.setup(with: Logger(primaryPrefix: "Session", level: .info))
Log.info("[AppDelegate] Setting up environment.")
/// Create a proper `SessionCallManager` for the main app (defaults to a no-op version)
Singleton.setCallManager(SessionCallManager(using: dependencies))
// Setup LibSession
LibSession.addLogger()
LibSession.createNetworkIfNeeded()
@ -97,7 +101,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
using: dependencies
)
if SessionEnvironment.shared?.callManager.wrappedValue?.currentCall == nil {
if Singleton.callManager.currentCall == nil {
UserDefaults.sharedLokiProject?[.isCallOngoing] = false
UserDefaults.sharedLokiProject?[.lastCallPreOffer] = nil
}
@ -687,7 +691,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// MARK: - Notifications
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
PushRegistrationManager.shared.didReceiveVanillaPushToken(deviceToken)
Singleton.pushRegistrationManager.didReceiveVanillaPushToken(deviceToken)
Log.info("Registering for push notifications.")
}
@ -696,9 +700,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
#if DEBUG
Log.warn("We're in debug mode. Faking success for remote registration with a fake push identifier.")
PushRegistrationManager.shared.didReceiveVanillaPushToken(Data(count: 32))
Singleton.pushRegistrationManager.didReceiveVanillaPushToken(Data(count: 32))
#else
PushRegistrationManager.shared.didFailToReceiveVanillaPushToken(error: error)
Singleton.pushRegistrationManager.didFailToReceiveVanillaPushToken(error: error)
#endif
}
@ -858,20 +862,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// MARK: - Call handling
func hasIncomingCallWaiting() -> Bool {
guard let call = AppEnvironment.shared.callManager.currentCall else { return false }
guard let call = Singleton.callManager.currentCall else { return false }
return !call.hasStartedConnecting
}
func hasCallOngoing() -> Bool {
guard let call = AppEnvironment.shared.callManager.currentCall else { return false }
guard let call = Singleton.callManager.currentCall else { return false }
return !call.hasEnded
}
func handleAppActivatedWithOngoingCallIfNeeded() {
guard
let call: SessionCall = (AppEnvironment.shared.callManager.currentCall as? SessionCall),
let call: SessionCall = (Singleton.callManager.currentCall as? SessionCall),
MiniCallView.current == nil,
Singleton.hasAppContext
else { return }

@ -23,9 +23,7 @@ public class AppEnvironment {
}
}
public var callManager: SessionCallManager
public var notificationPresenter: NotificationPresenter
public var pushRegistrationManager: PushRegistrationManager
// Stored properties cannot be marked as `@available`, only classes and functions.
// Instead, store a private `Any` and wrap it with a public `@available` getter
@ -36,9 +34,7 @@ public class AppEnvironment {
}
private init() {
self.callManager = SessionCallManager()
self.notificationPresenter = NotificationPresenter()
self.pushRegistrationManager = PushRegistrationManager()
self._userNotificationActionHandler = UserNotificationActionHandler()
SwiftSingletons.register(self)
@ -46,9 +42,6 @@ public class AppEnvironment {
public func setup() {
// Hang certain singletons on Environment too.
SessionEnvironment.shared?.callManager.mutate {
$0 = callManager
}
SessionEnvironment.shared?.notificationsManager.mutate {
$0 = notificationPresenter
}

@ -480,7 +480,7 @@ class NotificationActionHandler {
// MARK: -
func markAsRead(userInfo: [AnyHashable: Any]) -> AnyPublisher<Void, Error> {
func markAsRead(userInfo: [AnyHashable: Any], using dependencies: Dependencies) -> AnyPublisher<Void, Error> {
guard let threadId: String = userInfo[AppNotificationUserInfoKey.threadId] as? String else {
return Fail(error: NotificationError.failDebug("threadId was unexpectedly nil"))
.eraseToAnyPublisher()
@ -491,7 +491,7 @@ class NotificationActionHandler {
.eraseToAnyPublisher()
}
return markAsRead(threadId: threadId)
return markAsRead(threadId: threadId, using: dependencies)
}
func reply(
@ -532,7 +532,8 @@ class NotificationActionHandler {
db,
threadId: threadId,
threadVariant: thread.variant
)
),
using: dependencies
)
return try MessageSender.preparedSendData(
@ -590,7 +591,7 @@ class NotificationActionHandler {
.eraseToAnyPublisher()
}
private func markAsRead(threadId: String) -> AnyPublisher<Void, Error> {
private func markAsRead(threadId: String, using dependencies: Dependencies) -> AnyPublisher<Void, Error> {
return Storage.shared
.writePublisher { db in
guard
@ -617,7 +618,8 @@ class NotificationActionHandler {
db,
threadId: threadId,
threadVariant: threadVariant
)
),
using: dependencies
)
}
.eraseToAnyPublisher()

@ -8,17 +8,22 @@ import SessionMessagingKit
import SignalUtilitiesKit
import SessionUtilitiesKit
public enum PushRegistrationError: Error {
case assertionError(description: String)
case pushNotSupported(description: String)
case timeout
case publisherNoLongerExists
// MARK: - Singleton
public extension Singleton {
// FIXME: This will be reworked to be part of dependencies in the Groups Rebuild branch
fileprivate static var _pushRegistrationManager: Atomic<PushRegistrationManagerType> = Atomic(NoopPushRegistrationManager())
static var pushRegistrationManager: PushRegistrationManagerType { _pushRegistrationManager.wrappedValue }
static func setPushRegistrationManager(_ pushRegistrationManager: PushRegistrationManagerType) {
_pushRegistrationManager = Atomic(pushRegistrationManager)
}
}
/**
* Singleton used to integrate with push notification services - registration and routing received remote notifications.
*/
@objc public class PushRegistrationManager: NSObject, PKPushRegistryDelegate {
// MARK: - PushRegistrationManager
public class PushRegistrationManager: NSObject, PKPushRegistryDelegate, PushRegistrationManagerType {
private let dependencies: Dependencies
// MARK: - Dependencies
@ -26,27 +31,20 @@ public enum PushRegistrationError: Error {
return AppEnvironment.shared.notificationPresenter
}
// MARK: - Singleton class
@objc
public static var shared: PushRegistrationManager {
get {
return AppEnvironment.shared.pushRegistrationManager
}
}
override init() {
super.init()
SwiftSingletons.register(self)
}
private var vanillaTokenPublisher: AnyPublisher<Data, Error>?
private var vanillaTokenResolver: ((Result<Data, Error>) -> ())?
private var voipRegistry: PKPushRegistry?
private var voipTokenPublisher: AnyPublisher<Data?, Error>?
private var voipTokenResolver: ((Result<Data?, Error>) -> ())?
// MARK: - Initialization
public init(using dependencies: Dependencies) {
self.dependencies = dependencies
super.init()
}
// MARK: - Public interface
@ -72,7 +70,7 @@ public enum PushRegistrationError: Error {
// MARK: Vanilla push token
// Vanilla push token is obtained from the system via AppDelegate
public func didReceiveVanillaPushToken(_ tokenData: Data, using dependencies: Dependencies = Dependencies()) {
public func didReceiveVanillaPushToken(_ tokenData: Data) {
guard let vanillaTokenResolver = self.vanillaTokenResolver else {
Log.error("[PushRegistrationManager] Publisher completion in \(#function) unexpectedly nil")
return
@ -83,8 +81,8 @@ public enum PushRegistrationError: Error {
}
}
// Vanilla push token is obtained from the system via AppDelegate
public func didFailToReceiveVanillaPushToken(error: Error, using dependencies: Dependencies = Dependencies()) {
// Vanilla push token is obtained from the system via AppDelegate
public func didFailToReceiveVanillaPushToken(error: Error) {
guard let vanillaTokenResolver = self.vanillaTokenResolver else {
Log.error("[PushRegistrationManager] Publisher completion in \(#function) unexpectedly nil")
return
@ -286,7 +284,7 @@ public enum PushRegistrationError: Error {
let caller: String = payload["caller"] as? String,
let timestampMs: Int64 = payload["timestamp"] as? Int64
else {
SessionCallManager.reportFakeCall(info: "Missing payload data") // stringlint:ignore
SessionCallManager.reportFakeCall(info: "Missing payload data", using: dependencies) // stringlint:ignore
return
}
@ -314,7 +312,7 @@ public enum PushRegistrationError: Error {
}
}()
let call: SessionCall = SessionCall(db, for: caller, uuid: uuid, mode: .answer)
let call: SessionCall = SessionCall(db, for: caller, uuid: uuid, mode: .answer, using: dependencies)
let thread: SessionThread = try SessionThread.upsert(
db,
id: caller,
@ -342,7 +340,7 @@ public enum PushRegistrationError: Error {
}
guard let call: SessionCall = maybeCall else {
SessionCallManager.reportFakeCall(info: "Could not retrieve call from database") // stringlint:ignore
SessionCallManager.reportFakeCall(info: "Could not retrieve call from database", using: dependencies) // stringlint:ignore
return
}
@ -363,3 +361,12 @@ fileprivate extension Data {
return map { String(format: "%02hhx", $0) }.joined() // stringlint:ignore
}
}
// MARK: - PushRegistrationError
public enum PushRegistrationError: Error {
case assertionError(description: String)
case pushNotSupported(description: String)
case timeout
case publisherNoLongerExists
}

@ -0,0 +1,29 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import Combine
// FIXME: Remove this in Groups Rebuild (redundant with the updated dependency management)
public protocol PushRegistrationManagerType {
func createVoipRegistryIfNecessary()
func didReceiveVanillaPushToken(_ tokenData: Data)
func didFailToReceiveVanillaPushToken(error: Error)
func requestPushTokens() -> AnyPublisher<(pushToken: String, voipToken: String), Error>
}
// MARK: - NoopPushRegistrationManager
public class NoopPushRegistrationManager: PushRegistrationManagerType {
public func createVoipRegistryIfNecessary() {}
public func didReceiveVanillaPushToken(_ tokenData: Data) {}
public func didFailToReceiveVanillaPushToken(error: Error) {}
public func requestPushTokens() -> AnyPublisher<(pushToken: String, voipToken: String), Error> {
return Fail(
error: PushRegistrationError.assertionError(
description: "Attempted to register with NoopPushRegistrationManager" // stringlint:ignore
)
).eraseToAnyPublisher()
}
}

@ -104,7 +104,7 @@ public enum SyncPushTokensJob: JobExecutor {
/// **Note:** Apple's documentation states that we should re-register for notifications on every launch:
/// https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/HandlingRemoteNotifications.html#//apple_ref/doc/uid/TP40008194-CH6-SW1
Log.info("[SyncPushTokensJob] Re-registering for remote notifications")
PushRegistrationManager.shared.requestPushTokens()
Singleton.pushRegistrationManager.requestPushTokens()
.flatMap { (pushToken: String, voipToken: String) -> AnyPublisher<(String, String)?, Error> in
Deferred {
Future<(String, String)?, Error> { resolver in

@ -294,7 +294,7 @@ public class UserNotificationActionHandler: NSObject {
switch action {
case .markAsRead:
return actionHandler.markAsRead(userInfo: userInfo)
return actionHandler.markAsRead(userInfo: userInfo, using: dependencies)
case .reply:
guard let textInputResponse = response as? UNTextInputNotificationResponse else {

@ -119,7 +119,7 @@ struct DisplayNameScreen: View {
// If we are not in the registration flow then we are finished and should go straight
// to the home screen
guard self.flow == .register else {
self.flow.completeRegistration()
self.flow.completeRegistration(using: dependencies)
let homeVC: HomeVC = HomeVC(flow: self.flow, using: dependencies)
self.host.controller?.navigationController?.setViewControllers([ homeVC ], animated: true)

@ -108,7 +108,7 @@ struct LoadingScreen: View {
self.percentage = 1
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [dependencies] in
self.flow.completeRegistration()
self.flow.completeRegistration(using: dependencies)
let homeVC: HomeVC = HomeVC(flow: self.flow, using: dependencies)
self.host.controller?.navigationController?.setViewControllers([ homeVC ], animated: true)

@ -162,7 +162,8 @@ enum Onboarding {
db,
Contact.Columns.isTrusted.set(to: true), // Always trust the current user
Contact.Columns.isApproved.set(to: true),
Contact.Columns.didApproveMe.set(to: true)
Contact.Columns.didApproveMe.set(to: true),
using: dependencies
)
/// Create the 'Note to Self' thread (not visible by default)
@ -182,7 +183,8 @@ enum Onboarding {
.filter(id: x25519PublicKey)
.updateAllAndConfig(
db,
SessionThread.Columns.shouldBeVisible.set(to: false)
SessionThread.Columns.shouldBeVisible.set(to: false),
using: dependencies
)
}
@ -201,7 +203,7 @@ enum Onboarding {
.sinkUntilComplete()
}
func completeRegistration() {
func completeRegistration(using dependencies: Dependencies) {
// Set the `lastNameUpdate` to the current date, so that we don't overwrite
// what the user set in the display name step with whatever we find in their
// swarm (otherwise the user could enter a display name and have it immediately
@ -211,7 +213,8 @@ enum Onboarding {
.filter(id: getUserHexEncodedPublicKey(db))
.updateAllAndConfig(
db,
Profile.Columns.lastNameUpdate.set(to: Date().timeIntervalSince1970)
Profile.Columns.lastNameUpdate.set(to: Date().timeIntervalSince1970),
using: dependencies
)
}

@ -160,7 +160,7 @@ struct PNModeScreen: View {
}
private func finishRegister() {
self.flow.completeRegistration()
self.flow.completeRegistration(using: dependencies)
let homeVC: HomeVC = HomeVC(flow: self.flow, using: dependencies)
self.host.controller?.navigationController?.setViewControllers([ homeVC ], animated: true)

@ -205,12 +205,16 @@ public class BlockedContactsViewModel: SessionTableViewModel, NavigatableStateHo
confirmTitle: "blockUnblock".localized(),
confirmStyle: .danger,
cancelStyle: .alert_text
) { [weak self] _ in
) { [weak self, dependencies] _ in
// Unblock the contacts
Storage.shared.write { db in
_ = try Contact
.filter(ids: contactIds)
.updateAllAndConfig(db, Contact.Columns.isBlocked.set(to: false))
.updateAllAndConfig(
db,
Contact.Columns.isBlocked.set(to: false),
using: dependencies
)
}
self?.selectedContactIdsSubject.send([])

@ -139,7 +139,11 @@ class PrivacySettingsViewModel: SessionTableViewModel, NavigationItemSource, Nav
}
Storage.shared.write { db in
try db.setAndUpdateConfig(.isScreenLockEnabled, to: !db[.isScreenLockEnabled])
try db.setAndUpdateConfig(
.isScreenLockEnabled,
to: !db[.isScreenLockEnabled],
using: dependencies
)
}
}
)
@ -166,7 +170,8 @@ class PrivacySettingsViewModel: SessionTableViewModel, NavigationItemSource, Nav
Storage.shared.write { db in
try db.setAndUpdateConfig(
.checkForCommunityMessageRequests,
to: !db[.checkForCommunityMessageRequests]
to: !db[.checkForCommunityMessageRequests],
using: dependencies
)
}
}
@ -192,7 +197,11 @@ class PrivacySettingsViewModel: SessionTableViewModel, NavigationItemSource, Nav
),
onTap: {
Storage.shared.write { db in
try db.setAndUpdateConfig(.areReadReceiptsEnabled, to: !db[.areReadReceiptsEnabled])
try db.setAndUpdateConfig(
.areReadReceiptsEnabled,
to: !db[.areReadReceiptsEnabled],
using: dependencies
)
}
}
)
@ -252,7 +261,11 @@ class PrivacySettingsViewModel: SessionTableViewModel, NavigationItemSource, Nav
),
onTap: {
Storage.shared.write { db in
try db.setAndUpdateConfig(.typingIndicatorsEnabled, to: !db[.typingIndicatorsEnabled])
try db.setAndUpdateConfig(
.typingIndicatorsEnabled,
to: !db[.typingIndicatorsEnabled],
using: dependencies
)
}
}
)
@ -277,7 +290,11 @@ class PrivacySettingsViewModel: SessionTableViewModel, NavigationItemSource, Nav
),
onTap: {
Storage.shared.write { db in
try db.setAndUpdateConfig(.areLinkPreviewsEnabled, to: !db[.areLinkPreviewsEnabled])
try db.setAndUpdateConfig(
.areLinkPreviewsEnabled,
to: !db[.areLinkPreviewsEnabled],
using: dependencies
)
}
}
)
@ -314,7 +331,11 @@ class PrivacySettingsViewModel: SessionTableViewModel, NavigationItemSource, Nav
),
onTap: {
Storage.shared.write { db in
try db.setAndUpdateConfig(.areCallsEnabled, to: !db[.areCallsEnabled])
try db.setAndUpdateConfig(
.areCallsEnabled,
to: !db[.areCallsEnabled],
using: dependencies
)
}
}
)

@ -50,7 +50,8 @@ public extension UIContextualAction {
tableView: UITableView,
threadViewModel: SessionThreadViewModel,
viewController: UIViewController?,
navigatableStateHolder: NavigatableStateHolder?
navigatableStateHolder: NavigatableStateHolder?,
using dependencies: Dependencies
) -> [UIContextualAction]? {
guard !actions.isEmpty else { return nil }
@ -106,10 +107,11 @@ public extension UIContextualAction {
case true: threadViewModel.markAsRead(
target: .threadAndInteractions(
interactionsBeforeInclusive: threadViewModel.interactionId
)
),
using: dependencies
)
case false: threadViewModel.markAsUnread()
case false: threadViewModel.markAsUnread(using: dependencies)
}
}
completionHandler(true)
@ -142,7 +144,8 @@ public extension UIContextualAction {
db,
type: .deleteContactConversationAndMarkHidden,
threadId: threadViewModel.threadId,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
@ -188,7 +191,8 @@ public extension UIContextualAction {
db,
type: .hideContactConversation,
threadId: threadViewModel.threadId,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
@ -235,7 +239,8 @@ public extension UIContextualAction {
.updateAllAndConfig(
db,
SessionThread.Columns.pinnedPriority
.set(to: (threadViewModel.threadPinnedPriority == 0 ? 1 : 0))
.set(to: (threadViewModel.threadPinnedPriority == 0 ? 1 : 0)),
using: dependencies
)
}
}
@ -337,7 +342,11 @@ public extension UIContextualAction {
.save(db)
try Contact
.filter(id: threadViewModel.threadId)
.updateAllAndConfig(db, contactChanges)
.updateAllAndConfig(
db,
contactChanges,
using: dependencies
)
// Blocked message requests should be deleted
if threadIsMessageRequest {
@ -345,7 +354,8 @@ public extension UIContextualAction {
db,
type: .deleteContactConversationAndMarkHidden,
threadId: threadViewModel.threadId,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
}
@ -439,7 +449,8 @@ public extension UIContextualAction {
db,
type: deletionType,
threadId: threadViewModel.threadId,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
} catch {
DispatchQueue.main.async {
@ -551,7 +562,8 @@ public extension UIContextualAction {
db,
type: deletionType,
threadId: threadViewModel.threadId,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}

@ -2,11 +2,33 @@
import Foundation
import CallKit
import SessionUtilitiesKit
// MARK: - Singleton
public extension Singleton {
// FIXME: This will be reworked to be part of dependencies in the Groups Rebuild branch
fileprivate static var _callManager: Atomic<CallManagerProtocol> = Atomic(NoopSessionCallManager())
static var callManager: CallManagerProtocol { _callManager.wrappedValue }
static func setCallManager(_ callManager: CallManagerProtocol) {
_callManager = Atomic(callManager)
}
}
// MARK: - CallManagerProtocol
public protocol CallManagerProtocol {
var currentCall: CurrentCallProtocol? { get set }
func setCurrentCall(_ call: CurrentCallProtocol?)
func reportIncomingCall(_ call: CurrentCallProtocol, callerName: String, completion: @escaping (Error?) -> Void)
func reportCurrentCallEnded(reason: CXCallEndedReason?)
func suspendDatabaseIfCallEndedInBackground()
func startCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?)
func answerCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?)
func endCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?)
func showCallUIForCall(caller: String, uuid: String, mode: CallMode, interactionId: Int64?)
func handleICECandidates(message: CallMessage, sdpMLineIndexes: [UInt32], sdpMids: [String])

@ -3,6 +3,7 @@
import Foundation
import GRDB
import WebRTC
import SessionUtilitiesKit
public protocol CurrentCallProtocol {
var uuid: String { get }
@ -10,7 +11,7 @@ public protocol CurrentCallProtocol {
var hasStartedConnecting: Bool { get set }
var hasEnded: Bool { get set }
func updateCallMessage(mode: EndCallMode)
func updateCallMessage(mode: EndCallMode, using dependencies: Dependencies)
func didReceiveRemoteSDP(sdp: RTCSessionDescription)
func startSessionCall(_ db: Database)
}

@ -0,0 +1,25 @@
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import CallKit
internal struct NoopSessionCallManager: CallManagerProtocol {
var currentCall: CurrentCallProtocol?
func setCurrentCall(_ call: CurrentCallProtocol?) {}
func reportIncomingCall(_ call: CurrentCallProtocol, callerName: String, completion: @escaping (Error?) -> Void) {}
func reportCurrentCallEnded(reason: CXCallEndedReason?) {}
func suspendDatabaseIfCallEndedInBackground() {}
func startCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?) {}
func answerCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?) {}
func endCall(_ call: CurrentCallProtocol?, completion: ((Error?) -> Void)?) {}
func showCallUIForCall(caller: String, uuid: String, mode: CallMode, interactionId: Int64?) {}
func handleICECandidates(message: CallMessage, sdpMLineIndexes: [UInt32], sdpMids: [String]) {}
func handleAnswerMessage(_ message: CallMessage) {}
func currentWebRTCSessionMatches(callId: String) -> Bool { return false }
func dismissAllCallUI() {}
}

@ -197,7 +197,7 @@ enum _014_GenerateInitialUserConfigDumps: Migration {
// MARK: - Threads
try LibSession.updatingThreads(db, Array(allThreads.values))
try LibSession.updatingThreads(db, Array(allThreads.values), using: dependencies)
// MARK: - Syncing

@ -80,7 +80,7 @@ enum _018_DisappearingMessagesConfiguration: Migration {
}
// Update the configs so the settings are synced
_ = try LibSession.updatingDisappearingConfigs(db, contactUpdate)
_ = try LibSession.updatingDisappearingConfigs(db, contactUpdate, using: dependencies)
_ = try LibSession.batchUpdate(db, disappearingConfigs: legacyGroupUpdate, using: dependencies)
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration

@ -137,7 +137,11 @@ public extension BlindedIdLookup {
if isCheckingForOutbox && !contact.isApproved {
try Contact
.filter(id: contact.id)
.updateAllAndConfig(db, Contact.Columns.isApproved.set(to: true))
.updateAllAndConfig(
db,
Contact.Columns.isApproved.set(to: true),
using: dependencies
)
}
break

@ -115,13 +115,15 @@ public extension ClosedGroup {
_ db: Database? = nil,
threadId: String,
removeGroupData: Bool,
calledFromConfigHandling: Bool
calledFromConfigHandling: Bool,
using dependencies: Dependencies
) throws {
try removeKeysAndUnsubscribe(
db,
threadIds: [threadId],
removeGroupData: removeGroupData,
calledFromConfigHandling: calledFromConfigHandling
calledFromConfigHandling: calledFromConfigHandling,
using: dependencies
)
}
@ -129,16 +131,18 @@ public extension ClosedGroup {
_ db: Database? = nil,
threadIds: [String],
removeGroupData: Bool,
calledFromConfigHandling: Bool
calledFromConfigHandling: Bool,
using dependencies: Dependencies
) throws {
guard !threadIds.isEmpty else { return }
guard let db: Database = db else {
Storage.shared.write { db in
dependencies.storage.write { db in
try ClosedGroup.removeKeysAndUnsubscribe(
db,
threadIds: threadIds,
removeGroupData: removeGroupData,
calledFromConfigHandling: calledFromConfigHandling
calledFromConfigHandling: calledFromConfigHandling,
using: dependencies
)
}
return
@ -196,7 +200,8 @@ public extension ClosedGroup {
db,
legacyGroupIds: threadVariants
.filter { $0.variant == .legacyGroup }
.map { $0.id }
.map { $0.id },
using: dependencies
)
try LibSession.remove(

@ -491,7 +491,8 @@ public extension Interaction {
threadId: String,
threadVariant: SessionThread.Variant,
includingOlder: Bool,
trySendReadReceipt: Bool
trySendReadReceipt: Bool,
using dependencies: Dependencies
) throws {
guard let interactionId: Int64 = interactionId else { return }
@ -535,7 +536,8 @@ public extension Interaction {
],
lastReadTimestampMs: timestampMs,
trySendReadReceipt: trySendReadReceipt,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
return
}
@ -560,7 +562,8 @@ public extension Interaction {
interactionInfo: [interactionInfo],
lastReadTimestampMs: interactionInfo.timestampMs,
trySendReadReceipt: trySendReadReceipt,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
return
}
@ -576,7 +579,8 @@ public extension Interaction {
interactionInfo: interactionInfoToMarkAsRead,
lastReadTimestampMs: interactionInfo.timestampMs,
trySendReadReceipt: trySendReadReceipt,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
@ -647,7 +651,8 @@ public extension Interaction {
interactionInfo: [Interaction.ReadInfo],
lastReadTimestampMs: Int64,
trySendReadReceipt: Bool,
calledFromConfigHandling: Bool
calledFromConfigHandling: Bool,
using dependencies: Dependencies
) throws {
guard !interactionInfo.isEmpty else { return }
@ -657,7 +662,8 @@ public extension Interaction {
db,
threadId: threadId,
threadVariant: threadVariant,
lastReadTimestampMs: lastReadTimestampMs
lastReadTimestampMs: lastReadTimestampMs,
using: dependencies
)
// Add the 'DisappearingMessagesJob' if needed - this will update any expiring

@ -304,8 +304,9 @@ public extension SessionThread {
.filter(id: id)
.updateAllAndConfig(
db,
requiredChanges,
calledFromConfig: calledFromConfig,
requiredChanges
using: dependencies
)
/// We need to re-fetch the updated thread as the changes wouldn't have been applied to `result`, it's also possible additional
@ -423,13 +424,15 @@ public extension SessionThread {
_ db: Database,
type: SessionThread.DeletionType,
threadId: String,
calledFromConfigHandling: Bool
calledFromConfigHandling: Bool,
using dependencies: Dependencies
) throws {
try deleteOrLeave(
db,
type: type,
threadIds: [threadId],
calledFromConfigHandling: calledFromConfigHandling
calledFromConfigHandling: calledFromConfigHandling,
using: dependencies
)
}
@ -437,7 +440,8 @@ public extension SessionThread {
_ db: Database,
type: SessionThread.DeletionType,
threadIds: [String],
calledFromConfigHandling: Bool
calledFromConfigHandling: Bool,
using dependencies: Dependencies
) throws {
let currentUserPublicKey: String = getUserHexEncodedPublicKey(db)
let remainingThreadIds: Set<String> = threadIds.asSet().removing(currentUserPublicKey)
@ -450,9 +454,10 @@ public extension SessionThread {
.filter(id: currentUserPublicKey)
.updateAllAndConfig(
db,
calledFromConfig: calledFromConfigHandling,
SessionThread.Columns.pinnedPriority.set(to: LibSession.hiddenPriority),
SessionThread.Columns.shouldBeVisible.set(to: false)
SessionThread.Columns.shouldBeVisible.set(to: false),
calledFromConfig: calledFromConfigHandling,
using: dependencies
)
}
@ -461,9 +466,10 @@ public extension SessionThread {
.filter(ids: remainingThreadIds)
.updateAllAndConfig(
db,
calledFromConfig: calledFromConfigHandling,
SessionThread.Columns.pinnedPriority.set(to: LibSession.hiddenPriority),
SessionThread.Columns.shouldBeVisible.set(to: false)
SessionThread.Columns.shouldBeVisible.set(to: false),
calledFromConfig: calledFromConfigHandling,
using: dependencies
)
case .hideContactConversationAndDeleteContentDirectly:
@ -479,9 +485,10 @@ public extension SessionThread {
.filter(id: currentUserPublicKey)
.updateAllAndConfig(
db,
calledFromConfig: calledFromConfigHandling,
SessionThread.Columns.pinnedPriority.set(to: LibSession.hiddenPriority),
SessionThread.Columns.shouldBeVisible.set(to: false)
SessionThread.Columns.shouldBeVisible.set(to: false),
calledFromConfig: calledFromConfigHandling,
using: dependencies
)
}
@ -491,9 +498,10 @@ public extension SessionThread {
.filter(ids: remainingThreadIds)
.updateAllAndConfig(
db,
calledFromConfig: calledFromConfigHandling,
SessionThread.Columns.pinnedPriority.set(to: LibSession.hiddenPriority),
SessionThread.Columns.shouldBeVisible.set(to: false)
SessionThread.Columns.shouldBeVisible.set(to: false),
calledFromConfig: calledFromConfigHandling,
using: dependencies
)
case .deleteContactConversationAndMarkHidden:
@ -513,21 +521,22 @@ public extension SessionThread {
.filter(id: currentUserPublicKey)
.updateAllAndConfig(
db,
calledFromConfig: calledFromConfigHandling,
SessionThread.Columns.pinnedPriority.set(to: LibSession.hiddenPriority),
SessionThread.Columns.shouldBeVisible.set(to: false)
SessionThread.Columns.shouldBeVisible.set(to: false),
calledFromConfig: calledFromConfigHandling,
using: dependencies
)
}
if !calledFromConfigHandling {
// Update any other threads to be hidden
try LibSession.hide(db, contactIds: Array(remainingThreadIds))
try LibSession.hide(db, contactIds: Array(remainingThreadIds), using: dependencies)
}
case .deleteContactConversationAndContact:
// If this wasn't called from config handling then we need to hide the conversation
if !calledFromConfigHandling {
try LibSession.remove(db, contactIds: Array(remainingThreadIds))
try LibSession.remove(db, contactIds: Array(remainingThreadIds), using: dependencies)
}
_ = try SessionThread
@ -548,7 +557,8 @@ public extension SessionThread {
db,
threadIds: threadIds,
removeGroupData: true,
calledFromConfigHandling: calledFromConfigHandling
calledFromConfigHandling: calledFromConfigHandling,
using: dependencies
)
case .deleteCommunityAndContent:

@ -127,7 +127,8 @@ public enum GroupLeavingJob: JobExecutor {
db,
threadId: threadId,
removeGroupData: details.deleteThread,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}

@ -151,7 +151,8 @@ internal extension LibSession {
db,
type: .deleteContactConversationAndMarkHidden,
threadId: sessionId,
calledFromConfigHandling: true
calledFromConfigHandling: true,
using: dependencies
)
/// We need to create or update the thread
@ -250,10 +251,15 @@ internal extension LibSession {
db,
type: .deleteContactConversationAndContact,
threadIds: combinedIds,
calledFromConfigHandling: true
calledFromConfigHandling: true,
using: dependencies
)
try LibSession.remove(db, volatileContactIds: combinedIds)
try LibSession.remove(
db,
volatileContactIds: combinedIds,
using: dependencies
)
}
}
@ -365,7 +371,11 @@ internal extension LibSession {
// MARK: - Outgoing Changes
internal extension LibSession {
static func updatingContacts<T>(_ db: Database, _ updated: [T]) throws -> [T] {
static func updatingContacts<T>(
_ db: Database,
_ updated: [T],
using dependencies: Dependencies
) throws -> [T] {
guard let updatedContacts: [Contact] = updated as? [Contact] else { throw StorageError.generic }
// The current users contact data doesn't need to sync so exclude it, we also don't want to sync
@ -383,7 +393,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .contacts,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
// When inserting new contacts (or contacts with invalid profile data) we want
// to add any valid profile information we have so identify if any of the updated
@ -425,7 +436,11 @@ internal extension LibSession {
return updated
}
static func updatingProfiles<T>(_ db: Database, _ updated: [T]) throws -> [T] {
static func updatingProfiles<T>(
_ db: Database,
_ updated: [T],
using dependencies: Dependencies
) throws -> [T] {
guard let updatedProfiles: [Profile] = updated as? [Profile] else { throw StorageError.generic }
// We should only sync profiles which are associated to contact data to avoid including profiles
@ -456,7 +471,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .userProfile,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession.update(
profile: updatedUserProfile,
@ -468,7 +484,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .contacts,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession
.upsert(
@ -481,7 +498,11 @@ internal extension LibSession {
return updated
}
static func updatingDisappearingConfigs<T>(_ db: Database, _ updated: [T]) throws -> [T] {
static func updatingDisappearingConfigs<T>(
_ db: Database,
_ updated: [T],
using dependencies: Dependencies
) throws -> [T] {
guard let updatedDisappearingConfigs: [DisappearingMessagesConfiguration] = updated as? [DisappearingMessagesConfiguration] else { throw StorageError.generic }
// We should only sync disappearing messages configs which are associated to existing contacts
@ -510,7 +531,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .userProfile,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession.updateNoteToSelf(
disappearingMessagesConfig: updatedUserDisappearingConfig,
@ -522,7 +544,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .contacts,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession
.upsert(
@ -539,11 +562,16 @@ internal extension LibSession {
// MARK: - External Outgoing Changes
public extension LibSession {
static func hide(_ db: Database, contactIds: [String]) throws {
static func hide(
_ db: Database,
contactIds: [String],
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .contacts,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
// Mark the contacts as hidden
try LibSession.upsert(
@ -559,13 +587,18 @@ public extension LibSession {
}
}
static func remove(_ db: Database, contactIds: [String]) throws {
static func remove(
_ db: Database,
contactIds: [String],
using dependencies: Dependencies
) throws {
guard !contactIds.isEmpty else { return }
try LibSession.performAndPushChange(
db,
for: .contacts,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
contactIds.forEach { sessionId in
guard var cSessionId: [CChar] = sessionId.cString(using: .utf8) else { return }
@ -579,7 +612,8 @@ public extension LibSession {
static func update(
_ db: Database,
sessionId: String,
disappearingMessagesConfig: DisappearingMessagesConfiguration
disappearingMessagesConfig: DisappearingMessagesConfiguration,
using dependencies: Dependencies
) throws {
let userPublicKey: String = getUserHexEncodedPublicKey(db)
@ -588,7 +622,8 @@ public extension LibSession {
try LibSession.performAndPushChange(
db,
for: .userProfile,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession.updateNoteToSelf(
disappearingMessagesConfig: disappearingMessagesConfig,
@ -600,7 +635,8 @@ public extension LibSession {
try LibSession.performAndPushChange(
db,
for: .contacts,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession
.upsert(

@ -17,7 +17,8 @@ internal extension LibSession {
static func handleConvoInfoVolatileUpdate(
_ db: Database,
in conf: UnsafeMutablePointer<config_object>?,
mergeNeedsDump: Bool
mergeNeedsDump: Bool,
using dependencies: Dependencies
) throws {
guard mergeNeedsDump else { return }
guard conf != nil else { throw LibSessionError.nilConfigObject }
@ -97,7 +98,8 @@ internal extension LibSession {
interactionInfo: interactionInfoToMarkAsRead,
lastReadTimestampMs: lastReadTimestampMs,
trySendReadReceipt: false, // Interactions already read, no need to send
calledFromConfigHandling: true
calledFromConfigHandling: true,
using: dependencies
)
return nil
}
@ -226,7 +228,8 @@ internal extension LibSession {
static func updateMarkedAsUnreadState(
_ db: Database,
threads: [SessionThread]
threads: [SessionThread],
using dependencies: Dependencies
) throws {
// If we have no updated threads then no need to continue
guard !threads.isEmpty else { return }
@ -245,7 +248,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .convoInfoVolatile,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db),
using: dependencies
) { conf in
try upsert(
convoInfoVolatileChanges: changes,
@ -254,11 +258,16 @@ internal extension LibSession {
}
}
static func remove(_ db: Database, volatileContactIds: [String]) throws {
static func remove(
_ db: Database,
volatileContactIds: [String],
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .convoInfoVolatile,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
try volatileContactIds.forEach { contactId in
var cSessionId: [CChar] = try contactId.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
@ -269,11 +278,16 @@ internal extension LibSession {
}
}
static func remove(_ db: Database, volatileLegacyGroupIds: [String]) throws {
static func remove(
_ db: Database,
volatileLegacyGroupIds: [String],
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .convoInfoVolatile,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
try volatileLegacyGroupIds.forEach { legacyGroupId in
var cLegacyGroupId: [CChar] = try legacyGroupId.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
@ -284,11 +298,16 @@ internal extension LibSession {
}
}
static func remove(_ db: Database, volatileCommunityInfo: [OpenGroupUrlInfo]) throws {
static func remove(
_ db: Database,
volatileCommunityInfo: [OpenGroupUrlInfo],
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .convoInfoVolatile,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
try volatileCommunityInfo.forEach { urlInfo in
var cBaseUrl: [CChar] = try urlInfo.server.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
@ -308,12 +327,14 @@ public extension LibSession {
_ db: Database,
threadId: String,
threadVariant: SessionThread.Variant,
lastReadTimestampMs: Int64
lastReadTimestampMs: Int64,
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .convoInfoVolatile,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
try upsert(
convoInfoVolatileChanges: [

@ -14,8 +14,9 @@ public extension LibSession {
public typealias Domain = String
}
/// The default priority for newly created threads
static var defaultNewThreadPriority: Int32 { return visiblePriority }
/// The default priority for newly created threads - the default value is for threads to be hidden as we explicitly make threads visible
/// when sending or receiving messages
static var defaultNewThreadPriority: Int32 { return hiddenPriority }
/// A `0` `priority` value indicates visible, but not pinned
static let visiblePriority: Int32 = 0
@ -57,7 +58,7 @@ internal extension LibSession {
_ db: Database,
for variant: ConfigDump.Variant,
publicKey: String,
using dependencies: Dependencies = Dependencies(),
using dependencies: Dependencies,
change: (UnsafeMutablePointer<config_object>?) throws -> ()
) throws {
// Since we are doing direct memory manipulation we are using an `Atomic`
@ -99,7 +100,11 @@ internal extension LibSession {
}
}
@discardableResult static func updatingThreads<T>(_ db: Database, _ updated: [T]) throws -> [T] {
@discardableResult static func updatingThreads<T>(
_ db: Database,
_ updated: [T],
using dependencies: Dependencies
) throws -> [T] {
guard let updatedThreads: [SessionThread] = updated as? [SessionThread] else {
throw StorageError.generic
}
@ -115,7 +120,7 @@ internal extension LibSession {
.reduce(into: [:]) { result, next in result[next.threadId] = next }
// Update the unread state for the threads first (just in case that's what changed)
try LibSession.updateMarkedAsUnreadState(db, threads: updatedThreads)
try LibSession.updateMarkedAsUnreadState(db, threads: updatedThreads, using: dependencies)
// Then update the `hidden` and `priority` values
try groupedThreads.forEach { variant, threads in
@ -127,7 +132,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .userProfile,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession.updateNoteToSelf(
priority: {
@ -150,7 +156,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .contacts,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession.upsert(
contactData: remainingThreads
@ -174,7 +181,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .userGroups,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession.upsert(
communities: threads
@ -196,7 +204,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .userGroups,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession.upsert(
legacyGroups: threads
@ -240,7 +249,11 @@ internal extension LibSession {
}
}
static func updatingSetting(_ db: Database, _ updated: Setting?) throws {
static func updatingSetting(
_ db: Database,
_ updated: Setting?,
using dependencies: Dependencies
) throws {
// Don't current support any nullable settings
guard let updatedSetting: Setting = updated else { return }
@ -252,7 +265,8 @@ internal extension LibSession {
try LibSession.performAndPushChange(
db,
for: .userProfile,
publicKey: userPublicKey
publicKey: userPublicKey,
using: dependencies
) { conf in
try LibSession.updateSettings(
checkForCommunityMessageRequests: updatedSetting.unsafeValue(as: Bool.self),

@ -181,7 +181,8 @@ internal extension LibSession {
db,
type: .deleteCommunityAndContent,
threadIds: Array(communityIdsToRemove),
calledFromConfigHandling: true
calledFromConfigHandling: true,
using: dependencies
)
}
@ -365,13 +366,13 @@ internal extension LibSession {
if !legacyGroupIdsToRemove.isEmpty {
LibSession.kickFromConversationUIIfNeeded(removedThreadIds: Array(legacyGroupIdsToRemove))
try SessionThread
.deleteOrLeave(
db,
type: .deleteGroupAndContent,
threadIds: Array(legacyGroupIdsToRemove),
calledFromConfigHandling: true
)
try SessionThread.deleteOrLeave(
db,
type: .deleteGroupAndContent,
threadIds: Array(legacyGroupIdsToRemove),
calledFromConfigHandling: true,
using: dependencies
)
}
// MARK: -- Handle Group Changes
@ -546,12 +547,14 @@ public extension LibSession {
_ db: Database,
server: String,
rootToken: String,
publicKey: String
publicKey: String,
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .userGroups,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
try LibSession.upsert(
communities: [
@ -569,11 +572,17 @@ public extension LibSession {
}
}
static func remove(_ db: Database, server: String, roomToken: String) throws {
static func remove(
_ db: Database,
server: String,
roomToken: String,
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .userGroups,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
var cBaseUrl: [CChar] = try server.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
var cRoom: [CChar] = try roomToken.cString(using: .utf8) ?? { throw LibSessionError.invalidCConversion }()
@ -592,7 +601,8 @@ public extension LibSession {
roomToken: roomToken,
publicKey: ""
)
]
],
using: dependencies
)
}
@ -608,12 +618,14 @@ public extension LibSession {
latestKeyPairReceivedTimestamp: TimeInterval,
disappearingConfig: DisappearingMessagesConfiguration,
members: Set<String>,
admins: Set<String>
admins: Set<String>,
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .userGroups,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
guard conf != nil else { throw LibSessionError.nilConfigObject }
@ -674,12 +686,14 @@ public extension LibSession {
latestKeyPair: ClosedGroupKeyPair? = nil,
disappearingConfig: DisappearingMessagesConfiguration? = nil,
members: Set<String>? = nil,
admins: Set<String>? = nil
admins: Set<String>? = nil,
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .userGroups,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
try LibSession.upsert(
legacyGroups: [
@ -736,13 +750,18 @@ public extension LibSession {
}
}
static func remove(_ db: Database, legacyGroupIds: [String]) throws {
static func remove(
_ db: Database,
legacyGroupIds: [String],
using dependencies: Dependencies
) throws {
guard !legacyGroupIds.isEmpty else { return }
try LibSession.performAndPushChange(
db,
for: .userGroups,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db, using: dependencies),
using: dependencies
) { conf in
legacyGroupIds.forEach { threadId in
guard var cGroupId: [CChar] = threadId.cString(using: .utf8) else { return }
@ -753,7 +772,7 @@ public extension LibSession {
}
// Remove the volatile info as well
try LibSession.remove(db, volatileLegacyGroupIds: legacyGroupIds)
try LibSession.remove(db, volatileLegacyGroupIds: legacyGroupIds, using: dependencies)
}
// MARK: -- Group Changes

@ -93,7 +93,8 @@ internal extension LibSession {
db,
type: .hideContactConversation,
threadId: userPublicKey,
calledFromConfigHandling: true
calledFromConfigHandling: true,
using: dependencies
)
}
else {
@ -216,12 +217,14 @@ public extension LibSession {
static func updateNoteToSelf(
_ db: Database,
priority: Int32? = nil,
disappearingMessagesConfig: DisappearingMessagesConfiguration? = nil
disappearingMessagesConfig: DisappearingMessagesConfiguration? = nil,
using dependencies: Dependencies
) throws {
try LibSession.performAndPushChange(
db,
for: .userProfile,
publicKey: getUserHexEncodedPublicKey(db)
publicKey: getUserHexEncodedPublicKey(db),
using: dependencies
) { conf in
try LibSession.updateNoteToSelf(
priority: priority,

@ -52,17 +52,24 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
@discardableResult
func updateAllAndConfig(
_ db: Database,
_ assignments: ConfigColumnAssignment...,
calledFromConfig: Bool = false,
_ assignments: ConfigColumnAssignment...
using dependencies: Dependencies
) throws -> Int {
return try updateAllAndConfig(db, calledFromConfig: calledFromConfig, assignments)
return try updateAllAndConfig(
db,
assignments,
calledFromConfig: calledFromConfig,
using: dependencies
)
}
@discardableResult
func updateAllAndConfig(
_ db: Database,
_ assignments: [ConfigColumnAssignment],
calledFromConfig: Bool = false,
_ assignments: [ConfigColumnAssignment]
using dependencies: Dependencies
) throws -> Int {
let targetAssignments: [ColumnAssignment] = assignments.map { $0.assignment }
@ -71,7 +78,12 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
return try self.updateAll(db, targetAssignments)
}
return try self.updateAndFetchAllAndUpdateConfig(db, calledFromConfig: calledFromConfig, assignments).count
return try self.updateAndFetchAllAndUpdateConfig(
db,
assignments,
calledFromConfig: calledFromConfig,
using: dependencies
).count
}
// MARK: -- updateAndFetchAll
@ -79,17 +91,24 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
@discardableResult
func updateAndFetchAllAndUpdateConfig(
_ db: Database,
_ assignments: ConfigColumnAssignment...,
calledFromConfig: Bool = false,
_ assignments: ConfigColumnAssignment...
using dependencies: Dependencies
) throws -> [RowDecoder] {
return try updateAndFetchAllAndUpdateConfig(db, calledFromConfig: calledFromConfig, assignments)
return try updateAndFetchAllAndUpdateConfig(
db,
assignments,
calledFromConfig: calledFromConfig,
using: dependencies
)
}
@discardableResult
func updateAndFetchAllAndUpdateConfig(
_ db: Database,
_ assignments: [ConfigColumnAssignment],
calledFromConfig: Bool = false,
_ assignments: [ConfigColumnAssignment]
using dependencies: Dependencies
) throws -> [RowDecoder] {
// First perform the actual updates
let updatedData: [RowDecoder] = try self.updateAndFetchAll(db, assignments.map { $0.assignment })
@ -114,13 +133,13 @@ public extension QueryInterfaceRequest where RowDecoder: FetchableRecord & Table
// Update the config dump state where needed
switch self {
case is QueryInterfaceRequest<Contact>:
return try LibSession.updatingContacts(db, updatedData)
return try LibSession.updatingContacts(db, updatedData, using: dependencies)
case is QueryInterfaceRequest<Profile>:
return try LibSession.updatingProfiles(db, updatedData)
return try LibSession.updatingProfiles(db, updatedData, using: dependencies)
case is QueryInterfaceRequest<SessionThread>:
return try LibSession.updatingThreads(db, updatedData)
return try LibSession.updatingThreads(db, updatedData, using: dependencies)
default: return updatedData
}

@ -5,39 +5,103 @@ import GRDB
import SessionUtilitiesKit
public extension Database {
func setAndUpdateConfig(_ key: Setting.BoolKey, to newValue: Bool) throws {
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
func setAndUpdateConfig(
_ key: Setting.BoolKey,
to newValue: Bool,
using dependencies: Dependencies
) throws {
try updateConfigIfNeeded(
self,
key: key.rawValue,
updatedSetting: self.setting(key: key, to: newValue),
using: dependencies
)
}
func setAndUpdateConfig(_ key: Setting.DoubleKey, to newValue: Double?) throws {
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
func setAndUpdateConfig(
_ key: Setting.DoubleKey,
to newValue: Double?,
using dependencies: Dependencies
) throws {
try updateConfigIfNeeded(
self,
key: key.rawValue,
updatedSetting: self.setting(key: key, to: newValue),
using: dependencies
)
}
func setAndUpdateConfig(_ key: Setting.IntKey, to newValue: Int?) throws {
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
func setAndUpdateConfig(
_ key: Setting.IntKey,
to newValue: Int?,
using dependencies: Dependencies
) throws {
try updateConfigIfNeeded(
self,
key: key.rawValue,
updatedSetting: self.setting(key: key, to: newValue),
using: dependencies
)
}
func setAndUpdateConfig(_ key: Setting.StringKey, to newValue: String?) throws {
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
func setAndUpdateConfig(
_ key: Setting.StringKey,
to newValue: String?,
using dependencies: Dependencies
) throws {
try updateConfigIfNeeded(
self,
key: key.rawValue,
updatedSetting: self.setting(key: key, to: newValue),
using: dependencies
)
}
func setAndUpdateConfig<T: EnumIntSetting>(_ key: Setting.EnumKey, to newValue: T?) throws {
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
func setAndUpdateConfig<T: EnumIntSetting>(
_ key: Setting.EnumKey,
to newValue: T?,
using dependencies: Dependencies
) throws {
try updateConfigIfNeeded(
self,
key: key.rawValue,
updatedSetting: self.setting(key: key, to: newValue),
using: dependencies
)
}
func setAndUpdateConfig<T: EnumStringSetting>(_ key: Setting.EnumKey, to newValue: T?) throws {
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
func setAndUpdateConfig<T: EnumStringSetting>(
_ key: Setting.EnumKey,
to newValue: T?,
using dependencies: Dependencies
) throws {
try updateConfigIfNeeded(
self,
key: key.rawValue,
updatedSetting: self.setting(key: key, to: newValue),
using: dependencies
)
}
/// Value will be stored as a timestamp in seconds since 1970
func setAndUpdateConfig(_ key: Setting.DateKey, to newValue: Date?) throws {
try updateConfigIfNeeded(self, key: key.rawValue, updatedSetting: self.setting(key: key, to: newValue))
func setAndUpdateConfig(
_ key: Setting.DateKey,
to newValue: Date?,
using dependencies: Dependencies
) throws {
try updateConfigIfNeeded(
self,
key: key.rawValue,
updatedSetting: self.setting(key: key, to: newValue),
using: dependencies
)
}
private func updateConfigIfNeeded(
_ db: Database,
key: String,
updatedSetting: Setting?
updatedSetting: Setting?,
using dependencies: Dependencies
) throws {
// Before we do anything custom make sure the setting should trigger a change
guard LibSession.syncedSettings.contains(key) else { return }
@ -53,6 +117,6 @@ public extension Database {
}
}
try LibSession.updatingSetting(db, updatedSetting)
try LibSession.updatingSetting(db, updatedSetting, using: dependencies)
}
}

@ -79,7 +79,7 @@ public extension LibSession {
// If we weren't given a database instance then get one
guard let db: Database = db else {
Storage.shared.read { db in
dependencies.storage.read { db in
LibSession.loadState(db, userPublicKey: userPublicKey, ed25519SecretKey: secretKey, using: dependencies)
}
return
@ -465,7 +465,8 @@ public extension LibSession {
try LibSession.handleConvoInfoVolatileUpdate(
db,
in: conf,
mergeNeedsDump: config_needs_dump(conf)
mergeNeedsDump: config_needs_dump(conf),
using: dependencies
)
case .userGroups:

@ -240,7 +240,8 @@ public final class OpenGroupManager {
.updateAllAndConfig(
db,
OpenGroup.Columns.isActive.set(to: true),
OpenGroup.Columns.sequenceNumber.set(to: 0)
OpenGroup.Columns.sequenceNumber.set(to: 0),
using: dependencies
)
}
@ -290,7 +291,8 @@ public final class OpenGroupManager {
db,
server: server,
rootToken: roomToken,
publicKey: publicKey
publicKey: publicKey,
using: dependencies
)
}
@ -395,11 +397,15 @@ public final class OpenGroupManager {
// If it's a session-run room then just set it to inactive
_ = try? OpenGroup
.filter(id: openGroupId)
.updateAllAndConfig(db, OpenGroup.Columns.isActive.set(to: false))
.updateAllAndConfig(
db,
OpenGroup.Columns.isActive.set(to: false),
using: dependencies
)
}
if !calledFromConfigHandling, let server: String = server, let roomToken: String = roomToken {
try? LibSession.remove(db, server: server, roomToken: roomToken)
try? LibSession.remove(db, server: server, roomToken: roomToken, using: dependencies)
}
}
@ -478,7 +484,7 @@ public final class OpenGroupManager {
try OpenGroup
.filter(id: openGroup.id)
.updateAllAndConfig(db, changes)
.updateAllAndConfig(db, changes, using: dependencies)
// Update the admin/moderator group members
if let roomDetails: OpenGroupAPI.Room = pollInfo.details {
@ -764,7 +770,7 @@ public final class OpenGroupManager {
switch processedMessage {
case .config, .none: break
case .standard(let threadId, _, let proto, let messageInfo):
case .standard(_, _, let proto, let messageInfo):
// We want to update the BlindedIdLookup cache with the message info so we can avoid using the
// "expensive" lookup when possible
let lookup: BlindedIdLookup = try {

@ -25,7 +25,7 @@ extension MessageReceiver {
case .provisionalAnswer: break // TODO: Implement
case let .iceCandidates(sdpMLineIndexes, sdpMids):
SessionEnvironment.shared?.callManager.wrappedValue?.handleICECandidates(
Singleton.callManager.handleICECandidates(
message: message,
sdpMLineIndexes: sdpMLineIndexes,
sdpMids: sdpMids
@ -116,15 +116,12 @@ extension MessageReceiver {
return
}
// Ensure we have a call manager before continuing
guard let callManager: CallManagerProtocol = SessionEnvironment.shared?.callManager.wrappedValue else { return }
// Ignore pre offer message after the same call instance has been generated
if let currentCall: CurrentCallProtocol = callManager.currentCall, currentCall.uuid == message.uuid {
if let currentCall: CurrentCallProtocol = Singleton.callManager.currentCall, currentCall.uuid == message.uuid {
return
}
guard callManager.currentCall == nil else {
guard Singleton.callManager.currentCall == nil else {
try MessageReceiver.handleIncomingCallOfferInBusyState(db, message: message)
return
}
@ -132,7 +129,7 @@ extension MessageReceiver {
let interaction: Interaction? = try MessageReceiver.insertCallInfoMessage(db, for: message, using: dependencies)
// Handle UI
callManager.showCallUIForCall(
Singleton.callManager.showCallUIForCall(
caller: sender,
uuid: message.uuid,
mode: .answer,
@ -145,8 +142,7 @@ extension MessageReceiver {
// Ensure we have a call manager before continuing
guard
let callManager: CallManagerProtocol = SessionEnvironment.shared?.callManager.wrappedValue,
let currentCall: CurrentCallProtocol = callManager.currentCall,
let currentCall: CurrentCallProtocol = Singleton.callManager.currentCall,
currentCall.uuid == message.uuid,
let sdp: String = message.sdps.first
else { return }
@ -159,9 +155,8 @@ extension MessageReceiver {
SNLog("[Calls] Received answer message.")
guard
let callManager: CallManagerProtocol = SessionEnvironment.shared?.callManager.wrappedValue,
callManager.currentWebRTCSessionMatches(callId: message.uuid),
var currentCall: CurrentCallProtocol = callManager.currentCall,
Singleton.callManager.currentWebRTCSessionMatches(callId: message.uuid),
var currentCall: CurrentCallProtocol = Singleton.callManager.currentCall,
currentCall.uuid == message.uuid,
let sender: String = message.sender
else { return }
@ -169,8 +164,8 @@ extension MessageReceiver {
guard sender != getUserHexEncodedPublicKey(db) else {
guard !currentCall.hasStartedConnecting else { return }
callManager.dismissAllCallUI()
callManager.reportCurrentCallEnded(reason: .answeredElsewhere)
Singleton.callManager.dismissAllCallUI()
Singleton.callManager.reportCurrentCallEnded(reason: .answeredElsewhere)
return
}
guard let sdp: String = message.sdps.first else { return }
@ -178,22 +173,21 @@ extension MessageReceiver {
let sdpDescription: RTCSessionDescription = RTCSessionDescription(type: .answer, sdp: sdp)
currentCall.hasStartedConnecting = true
currentCall.didReceiveRemoteSDP(sdp: sdpDescription)
callManager.handleAnswerMessage(message)
Singleton.callManager.handleAnswerMessage(message)
}
private static func handleEndCallMessage(_ db: Database, message: CallMessage) {
SNLog("[Calls] Received end call message.")
guard
let callManager: CallManagerProtocol = SessionEnvironment.shared?.callManager.wrappedValue,
callManager.currentWebRTCSessionMatches(callId: message.uuid),
let currentCall: CurrentCallProtocol = callManager.currentCall,
Singleton.callManager.currentWebRTCSessionMatches(callId: message.uuid),
let currentCall: CurrentCallProtocol = Singleton.callManager.currentCall,
currentCall.uuid == message.uuid,
let sender: String = message.sender
else { return }
callManager.dismissAllCallUI()
callManager.reportCurrentCallEnded(
Singleton.callManager.dismissAllCallUI()
Singleton.callManager.reportCurrentCallEnded(
reason: (sender == getUserHexEncodedPublicKey(db) ?
.declinedElsewhere :
.remoteEnded

@ -31,7 +31,8 @@ extension MessageReceiver {
db,
threadId: threadId,
threadVariant: threadVariant,
message: message
message: message,
using: dependencies
)
case .membersAdded:
@ -39,7 +40,8 @@ extension MessageReceiver {
db,
threadId: threadId,
threadVariant: threadVariant,
message: message
message: message,
using: dependencies
)
case .membersRemoved:
@ -47,7 +49,8 @@ extension MessageReceiver {
db,
threadId: threadId,
threadVariant: threadVariant,
message: message
message: message,
using: dependencies
)
case .memberLeft:
@ -55,7 +58,8 @@ extension MessageReceiver {
db,
threadId: threadId,
threadVariant: threadVariant,
message: message
message: message,
using: dependencies
)
case .encryptionKeyPairRequest: break // Currently not used
@ -234,7 +238,8 @@ extension MessageReceiver {
latestKeyPairReceivedTimestamp: receivedTimestamp,
disappearingConfig: disappearingConfig,
members: members.asSet(),
admins: admins.asSet()
admins: admins.asSet(),
using: dependencies
)
}
@ -336,7 +341,8 @@ extension MessageReceiver {
try? LibSession.update(
db,
groupPublicKey: groupPublicKey,
latestKeyPair: keyPair
latestKeyPair: keyPair,
using: dependencies
)
}
catch {
@ -354,7 +360,8 @@ extension MessageReceiver {
_ db: Database,
threadId: String,
threadVariant: SessionThread.Variant,
message: ClosedGroupControlMessage
message: ClosedGroupControlMessage,
using dependencies: Dependencies
) throws {
guard
let messageKind: ClosedGroupControlMessage.Kind = message.kind,
@ -373,7 +380,8 @@ extension MessageReceiver {
try? LibSession.update(
db,
groupPublicKey: threadId,
name: name
name: name,
using: dependencies
)
_ = try ClosedGroup
@ -390,7 +398,8 @@ extension MessageReceiver {
_ db: Database,
threadId: String,
threadVariant: SessionThread.Variant,
message: ClosedGroupControlMessage
message: ClosedGroupControlMessage,
using dependencies: Dependencies
) throws {
guard
let messageKind: ClosedGroupControlMessage.Kind = message.kind,
@ -424,7 +433,8 @@ extension MessageReceiver {
admins: allMembers
.filter { $0.role == .admin }
.map { $0.profileId }
.asSet()
.asSet(),
using: dependencies
)
// Create records for any new members
@ -479,7 +489,8 @@ extension MessageReceiver {
_ db: Database,
threadId: String,
threadVariant: SessionThread.Variant,
message: ClosedGroupControlMessage
message: ClosedGroupControlMessage,
using dependencies: Dependencies
) throws {
guard
let messageKind: ClosedGroupControlMessage.Kind = message.kind,
@ -531,7 +542,8 @@ extension MessageReceiver {
admins: allMembers
.filter { $0.role == .admin }
.map { $0.profileId }
.asSet()
.asSet(),
using: dependencies
)
// Delete the removed members
@ -552,7 +564,8 @@ extension MessageReceiver {
db,
threadId: threadId,
removeGroupData: true,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
}
@ -567,7 +580,8 @@ extension MessageReceiver {
_ db: Database,
threadId: String,
threadVariant: SessionThread.Variant,
message: ClosedGroupControlMessage
message: ClosedGroupControlMessage,
using dependencies: Dependencies
) throws {
guard
let messageKind: ClosedGroupControlMessage.Kind = message.kind,
@ -606,7 +620,8 @@ extension MessageReceiver {
admins: allMembers
.filter { $0.role == .admin }
.map { $0.profileId }
.asSet()
.asSet(),
using: dependencies
)
// Delete the members to remove
@ -620,7 +635,8 @@ extension MessageReceiver {
db,
threadId: threadId,
removeGroupData: (sender == userPublicKey),
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}

@ -12,7 +12,8 @@ extension MessageReceiver {
threadVariant: SessionThread.Variant,
message: ExpirationTimerUpdate,
serverExpirationTimestamp: TimeInterval?,
proto: SNProtoContent
proto: SNProtoContent,
using dependencies: Dependencies
) throws {
guard proto.hasExpirationType || proto.hasExpirationTimer else { return }
guard
@ -52,7 +53,8 @@ extension MessageReceiver {
.update(
db,
groupPublicKey: threadId,
disappearingConfig: updatedConfig
disappearingConfig: updatedConfig,
using: dependencies
)
}
fallthrough
@ -81,7 +83,8 @@ extension MessageReceiver {
_ db: Database,
messageVariant: Message.Variant?,
contactId: String?,
version: FeatureVersion?
version: FeatureVersion?,
using dependencies: Dependencies
) {
guard
let messageVariant: Message.Variant = messageVariant,
@ -97,7 +100,8 @@ extension MessageReceiver {
.filter(id: contactId)
.updateAllAndConfig(
db,
Contact.Columns.lastKnownClientVersion.set(to: version)
Contact.Columns.lastKnownClientVersion.set(to: version),
using: dependencies
)
}
}

@ -118,7 +118,8 @@ extension MessageReceiver {
db,
type: .deleteContactConversationAndContact, // Blinded contact isn't synced anyway
threadId: blindedIdLookup.blindedId,
calledFromConfigHandling: false
calledFromConfigHandling: false,
using: dependencies
)
}
@ -126,7 +127,8 @@ extension MessageReceiver {
try updateContactApprovalStatusIfNeeded(
db,
senderSessionId: senderId,
threadId: nil
threadId: nil,
using: dependencies
)
// If there were blinded contacts which have now been resolved to this contact then we should remove
@ -140,7 +142,8 @@ extension MessageReceiver {
try updateContactApprovalStatusIfNeeded(
db,
senderSessionId: userPublicKey,
threadId: unblindedThread.id
threadId: unblindedThread.id,
using: dependencies
)
}
@ -165,7 +168,8 @@ extension MessageReceiver {
internal static func updateContactApprovalStatusIfNeeded(
_ db: Database,
senderSessionId: String,
threadId: String?
threadId: String?,
using dependencies: Dependencies
) throws {
let userPublicKey: String = getUserHexEncodedPublicKey(db)
@ -188,7 +192,11 @@ extension MessageReceiver {
try? contact.save(db)
_ = try? Contact
.filter(id: threadId)
.updateAllAndConfig(db, Contact.Columns.isApproved.set(to: true))
.updateAllAndConfig(
db,
Contact.Columns.isApproved.set(to: true),
using: dependencies
)
}
else {
// The message was sent to the current user so flag their 'didApproveMe' as true (can't send a message to
@ -200,7 +208,11 @@ extension MessageReceiver {
try? contact.save(db)
_ = try? Contact
.filter(id: senderSessionId)
.updateAllAndConfig(db, Contact.Columns.didApproveMe.set(to: true))
.updateAllAndConfig(
db,
Contact.Columns.didApproveMe.set(to: true),
using: dependencies
)
}
}
}

@ -10,7 +10,8 @@ extension MessageReceiver {
_ db: Database,
threadId: String,
threadVariant: SessionThread.Variant,
message: UnsendRequest
message: UnsendRequest,
using dependencies: Dependencies
) throws {
let userPublicKey: String = getUserHexEncodedPublicKey(db)
@ -35,7 +36,8 @@ extension MessageReceiver {
threadId: interaction.threadId,
threadVariant: threadVariant,
includingOlder: false,
trySendReadReceipt: false
trySendReadReceipt: false,
using: dependencies
)
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: interaction.notificationIdentifiers)

@ -13,7 +13,7 @@ extension MessageReceiver {
message: VisibleMessage,
serverExpirationTimestamp: TimeInterval?,
associatedWithProto proto: SNProtoContent,
using dependencies: Dependencies = Dependencies()
using dependencies: Dependencies
) throws -> Int64 {
guard let sender: String = message.sender, let dataMessage = proto.dataMessage else {
throw MessageReceiverError.invalidMessage
@ -212,7 +212,8 @@ extension MessageReceiver {
interactionId: existingInteractionId,
messageSentTimestamp: messageSentTimestamp,
variant: variant,
syncTarget: message.syncTarget
syncTarget: message.syncTarget,
using: dependencies
)
Message.getExpirationForOutgoingDisappearingMessages(
@ -239,7 +240,8 @@ extension MessageReceiver {
interactionId: interactionId,
messageSentTimestamp: messageSentTimestamp,
variant: variant,
syncTarget: message.syncTarget
syncTarget: message.syncTarget,
using: dependencies
)
if messageExpirationInfo.shouldUpdateExpiry {
@ -356,7 +358,8 @@ extension MessageReceiver {
try MessageReceiver.updateContactApprovalStatusIfNeeded(
db,
senderSessionId: sender,
threadId: thread.id
threadId: thread.id,
using: dependencies
)
}
@ -463,7 +466,8 @@ extension MessageReceiver {
interactionId: Int64,
messageSentTimestamp: TimeInterval,
variant: Interaction.Variant,
syncTarget: String?
syncTarget: String?,
using dependencies: Dependencies
) throws {
guard variant == .standardOutgoing else { return }
@ -487,7 +491,8 @@ extension MessageReceiver {
threadId: thread.id,
threadVariant: thread.variant,
includingOlder: true,
trySendReadReceipt: false
trySendReadReceipt: false,
using: dependencies
)
// Process any PendingReadReceipt values

@ -93,7 +93,8 @@ extension MessageSender {
latestKeyPairReceivedTimestamp: latestKeyPairReceivedTimestamp,
disappearingConfig: DisappearingMessagesConfiguration.defaultWith(groupPublicKey),
members: members,
admins: admins
admins: admins,
using: dependencies
)
let memberSendData: [MessageSender.PreparedSendData] = try members
@ -274,7 +275,8 @@ extension MessageSender {
admins: allGroupMembers
.filter { $0.role == .admin }
.map { $0.profileId }
.asSet()
.asSet(),
using: dependencies
)
}
@ -344,7 +346,8 @@ extension MessageSender {
try? LibSession.update(
db,
groupPublicKey: closedGroup.threadId,
name: name
name: name,
using: dependencies
)
}
@ -457,7 +460,8 @@ extension MessageSender {
admins: allGroupMembers
.filter { $0.role == .admin }
.map { $0.profileId }
.asSet()
.asSet(),
using: dependencies
)
// Send the update to the group

@ -282,7 +282,8 @@ public enum MessageReceiver {
version: ((!proto.hasExpirationType && !proto.hasExpirationTimer) ?
.legacyDisappearingMessages :
.newDisappearingMessages
)
),
using: dependencies
)
switch message {
@ -327,7 +328,8 @@ public enum MessageReceiver {
threadVariant: threadVariant,
message: message,
serverExpirationTimestamp: serverExpirationTimestamp,
proto: proto
proto: proto,
using: dependencies
)
case let message as UnsendRequest:
@ -335,7 +337,8 @@ public enum MessageReceiver {
db,
threadId: threadId,
threadVariant: threadVariant,
message: message
message: message,
using: dependencies
)
case let message as CallMessage:
@ -361,21 +364,29 @@ public enum MessageReceiver {
threadVariant: threadVariant,
message: message,
serverExpirationTimestamp: serverExpirationTimestamp,
associatedWithProto: proto
associatedWithProto: proto,
using: dependencies
)
default: throw MessageReceiverError.unknownMessage
}
// Perform any required post-handling logic
try MessageReceiver.postHandleMessage(db, threadId: threadId, threadVariant: threadVariant, message: message)
try MessageReceiver.postHandleMessage(
db,
threadId: threadId,
threadVariant: threadVariant,
message: message,
using: dependencies
)
}
public static func postHandleMessage(
_ db: Database,
threadId: String,
threadVariant: SessionThread.Variant,
message: Message
message: Message,
using dependencies: Dependencies
) throws {
// When handling any message type which has related UI we want to make sure the thread becomes
// visible (the only other spot this flag gets set is when sending messages)
@ -424,7 +435,8 @@ public enum MessageReceiver {
.updateAllAndConfig(
db,
SessionThread.Columns.shouldBeVisible.set(to: true),
SessionThread.Columns.pinnedPriority.set(to: LibSession.visiblePriority)
SessionThread.Columns.pinnedPriority.set(to: LibSession.visiblePriority),
using: dependencies
)
}
}

@ -272,7 +272,7 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
}
/// This method marks a thread as read and depending on the target may also update the interactions within a thread as read
public func markAsRead(target: ReadTarget) {
public func markAsRead(target: ReadTarget, using dependencies: Dependencies) {
// Store the logic to mark a thread as read (to paths need to run this)
let threadId: String = self.threadId
let threadWasMarkedUnread: Bool? = self.threadWasMarkedUnread
@ -286,7 +286,8 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
.filter(id: threadId)
.updateAllAndConfig(
db,
SessionThread.Columns.markedAsUnread.set(to: false)
SessionThread.Columns.markedAsUnread.set(to: false),
using: dependencies
)
}
}
@ -327,14 +328,15 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
threadVariant: threadVariant,
isBlocked: threadIsBlocked,
isMessageRequest: threadIsMessageRequest
)
),
using: dependencies
)
}
}
}
/// This method will mark a thread as read
public func markAsUnread() {
public func markAsUnread(using dependencies: Dependencies) {
guard self.threadWasMarkedUnread != true else { return }
let threadId: String = self.threadId
@ -344,7 +346,8 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
.filter(id: threadId)
.updateAllAndConfig(
db,
SessionThread.Columns.markedAsUnread.set(to: true)
SessionThread.Columns.markedAsUnread.set(to: true),
using: dependencies
)
}
}

@ -605,7 +605,7 @@ public struct ProfileManager {
else {
try Profile
.filter(id: publicKey)
.updateAllAndConfig(db, profileChanges)
.updateAllAndConfig(db, profileChanges, using: dependencies)
}
}

@ -11,9 +11,6 @@ public class SessionEnvironment {
public let windowManager: OWSWindowManager
public var isRequestingPermission: Bool
// Note: This property is configured after Environment is created.
public let callManager: Atomic<CallManagerProtocol?> = Atomic(nil)
// Note: This property is configured after Environment is created.
public let notificationsManager: Atomic<NotificationsProtocol?> = Atomic(nil)

@ -9,7 +9,7 @@ import Nimble
@testable import SessionMessagingKit
@testable import SessionUtilitiesKit
extension Job: MutableIdentifiable {
extension Job: @retroactive MutableIdentifiable {
public mutating func setId(_ id: Int64?) { self.id = id }
}
@ -67,7 +67,10 @@ class MessageSendJobSpec: QuickSpec {
db,
id: "Test1",
variant: .contact,
values: SessionThread.TargetValues(shouldBeVisible: .setTo(true)),
values: SessionThread.TargetValues(
// False is the default and will mean we don't need libSession loaded
shouldBeVisible: .setTo(false)
),
calledFromConfig: false,
using: dependencies
)

@ -199,7 +199,8 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
db,
threadId: threadId,
threadVariant: threadVariant,
message: messageInfo.message
message: messageInfo.message,
using: dependencies
)
return self?.handleSuccessForIncomingCall(db, for: callMessage)
@ -210,7 +211,8 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
db,
threadId: threadId,
threadVariant: threadVariant,
message: messageInfo.message
message: messageInfo.message,
using: dependencies
)
case .standard(let threadId, let threadVariant, let proto, let messageInfo):

@ -274,7 +274,8 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
.updateAllAndConfig(
db,
SessionThread.Columns.shouldBeVisible.set(to: true),
SessionThread.Columns.pinnedPriority.set(to: LibSession.visiblePriority)
SessionThread.Columns.pinnedPriority.set(to: LibSession.visiblePriority),
using: dependencies
)
}

Loading…
Cancel
Save