diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift
index 70efe23dc..88dcaae4b 100644
--- a/Session/Conversations/ConversationVC+Interaction.swift
+++ b/Session/Conversations/ConversationVC+Interaction.swift
@@ -1310,9 +1310,10 @@ extension ConversationVC:
},
onCancel: { [weak self] modal in
UIPasteboard.general.string = url.absoluteString
- self?.showInputAccessoryView()
- modal.dismiss(animated: true)
+ modal.dismiss(animated: true) {
+ self?.showInputAccessoryView()
+ }
}
)
)
diff --git a/Session/Meta/MainAppContext.swift b/Session/Meta/MainAppContext.swift
index 4ae9c3fd4..84dc938ae 100644
--- a/Session/Meta/MainAppContext.swift
+++ b/Session/Meta/MainAppContext.swift
@@ -147,6 +147,12 @@ final class MainAppContext: AppContext {
// stringlint:ignore_contents
func ensureSleepBlocking(_ shouldBeBlocking: Bool, blockingObjects: [Any]) {
+ guard Thread.isMainThread else {
+ return DispatchQueue.main.async { [weak self] in
+ self?.ensureSleepBlocking(shouldBeBlocking, blockingObjects: blockingObjects)
+ }
+ }
+
if UIApplication.shared.isIdleTimerDisabled != shouldBeBlocking {
if shouldBeBlocking {
var logString: String = "Blocking sleep because of: \(String(describing: blockingObjects.first))"
diff --git a/Session/Meta/Translations/Localizable.xcstrings b/Session/Meta/Translations/Localizable.xcstrings
index 39ea188ab..1d95ed80f 100644
--- a/Session/Meta/Translations/Localizable.xcstrings
+++ b/Session/Meta/Translations/Localizable.xcstrings
@@ -74075,7 +74075,7 @@
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Your IP is visible to your call partner and an Oxen Foundation server while using beta calls."
+ "value" : "Your IP is visible to your call partner and a Session Technology Foundation server while using beta calls."
}
},
"eo" : {
@@ -232561,6 +232561,61 @@
}
}
},
+ "legacyGroupAfterDeprecationAdmin" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "This group is now read-only. Recreate this group to keep chatting."
+ }
+ }
+ }
+ },
+ "legacyGroupAfterDeprecationMember" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "This group is now read-only. Ask the group admin to recreate this group to keep chatting."
+ }
+ }
+ }
+ },
+ "legacyGroupBeforeDeprecationAdmin" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Groups have been upgraded! Recreate this group for improved reliability. This group will become read-only on {date}."
+ }
+ }
+ }
+ },
+ "legacyGroupBeforeDeprecationMember" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Groups have been upgraded! Ask the group admin to recreate this group for improved reliability. This group will become read-only on {date}."
+ }
+ }
+ }
+ },
+ "legacyGroupChatHistory" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Chat history will not be transferred to the new group. You can still view all chat history in your old group."
+ }
+ }
+ }
+ },
"legacyGroupMemberNew" : {
"extractionState" : "manual",
"localizations" : {
@@ -354496,6 +354551,17 @@
}
}
},
+ "recreateGroup" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Recreate Group"
+ }
+ }
+ }
+ },
"redo" : {
"extractionState" : "manual",
"localizations" : {
@@ -379053,6 +379119,17 @@
}
}
},
+ "shareExtensionNoAccountError" : {
+ "extractionState" : "manual",
+ "localizations" : {
+ "en" : {
+ "stringUnit" : {
+ "state" : "translated",
+ "value" : "Oops! Looks like you don't have a {app_name} account yet.
You'll need to create one in the {app_name} app before you can share."
+ }
+ }
+ }
+ },
"shareToSession" : {
"extractionState" : "manual",
"localizations" : {
diff --git a/Session/Onboarding/LandingScreen.swift b/Session/Onboarding/LandingScreen.swift
index ea67bac3f..26a60d322 100644
--- a/Session/Onboarding/LandingScreen.swift
+++ b/Session/Onboarding/LandingScreen.swift
@@ -193,10 +193,11 @@ struct LandingScreen: View {
UIApplication.shared.open(url)
}
},
- onCancel: { _ in
+ onCancel: { modal in
if let url: URL = URL(string: "https://getsession.org/privacy-policy") {
UIApplication.shared.open(url)
}
+ modal.close()
}
)
)
diff --git a/SessionMessagingKit/Jobs/GarbageCollectionJob.swift b/SessionMessagingKit/Jobs/GarbageCollectionJob.swift
index 732765fba..99744d96e 100644
--- a/SessionMessagingKit/Jobs/GarbageCollectionJob.swift
+++ b/SessionMessagingKit/Jobs/GarbageCollectionJob.swift
@@ -361,9 +361,7 @@ public enum GarbageCollectionJob: JobExecutor {
},
completion: { _ in
// Dispatch async so we can swap from the write queue to a read one (we are done
- // writing), this has to be done after a slight delay to ensure the transaction
- // provided by the completion block completes first (ie. so we don't hit
- // re-entrancy issues)
+ // writing)
scheduler.schedule {
// Retrieve a list of all valid attachmnet and avatar file paths
let maybeFileInfo: FileInfo? = dependencies[singleton: .storage].read { db -> FileInfo in
diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift
index a1e2a71c9..c18f94b7e 100644
--- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift
+++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift
@@ -777,7 +777,7 @@ public final class MessageSender {
/// If we have affected rows then we should update them with the latest error text
///
- /// **Note:** We `writeAsync` here as performing a syncronous `wrute` results in a reentrancy assertion
+ /// **Note:** We `writeAsync` here as performing a syncronous `write` results in a reentrancy assertion
dependencies[singleton: .storage].writeAsync { db in
let targetState: Interaction.State
switch destination {
diff --git a/SessionUtilitiesKit/Database/Storage.swift b/SessionUtilitiesKit/Database/Storage.swift
index bd095a590..3cd1243c3 100644
--- a/SessionUtilitiesKit/Database/Storage.swift
+++ b/SessionUtilitiesKit/Database/Storage.swift
@@ -610,6 +610,9 @@ open class Storage {
case StorageError.databaseSuspended:
Log.error(.storage, "Database \(isWrite ? "write" : "read") failed as the database is suspended.")
+ case StorageError.transactionDeadlockTimeout:
+ Log.critical("[Storage] Database \(isWrite ? "write" : "read") failed due to a potential synchronous query deadlock timeout.")
+
default: break
}
}
@@ -680,7 +683,7 @@ open class Storage {
case (.invalid(let error), _):
result = .failure(error)
semaphore?.signal()
-
+
case (.valid(let dbWriter), true):
dbWriter.asyncWrite(
{ db in result = .success(try Storage.track(db, info, operation)) },
@@ -716,7 +719,7 @@ open class Storage {
/// If this is a synchronous operation then `semaphore` will exist and will block here waiting on the signal from one of the
/// above closures to be sent
let semaphoreResult: DispatchTimeoutResult? = semaphore?.wait(timeout: .now() + .seconds(Storage.transactionDeadlockTimeoutSeconds))
-
+
/// If the transaction timed out then log the error and report a failure
guard semaphoreResult != .timedOut else {
StorageState.logIfNeeded(StorageError.transactionDeadlockTimeout, isWrite: info.isWrite)
@@ -895,51 +898,55 @@ public extension Publisher where Failure == Error {
// MARK: - CallInfo
private extension Storage {
- class CallInfo {
- enum Behaviour {
- case syncRead
- case asyncRead
- case syncWrite
- case asyncWrite
- }
-
- weak var storage: Storage?
- let file: String
- let function: String
- let line: Int
- let behaviour: Behaviour
-
- var callInfo: String {
- let fileInfo: String = (file.components(separatedBy: "/").last.map { "\($0):\(line) - " } ?? "")
+ // MARK: - CallInfo
+
+ private extension Storage {
+ class CallInfo {
+ enum Behaviour {
+ case syncRead
+ case asyncRead
+ case syncWrite
+ case asyncWrite
+ }
- return "\(fileInfo)\(function)"
- }
-
- var isWrite: Bool {
- switch behaviour {
- case .syncWrite, .asyncWrite: return true
- case .syncRead, .asyncRead: return false
+ weak var storage: Storage?
+ let file: String
+ let function: String
+ let line: Int
+ let behaviour: Behaviour
+
+ var callInfo: String {
+ let fileInfo: String = (file.components(separatedBy: "/").last.map { "\($0):\(line) - " } ?? "")
+
+ return "\(fileInfo)\(function)"
}
- }
- var isAsync: Bool {
- switch behaviour {
- case .asyncRead, .asyncWrite: return true
- case .syncRead, .syncWrite: return false
+
+ var isWrite: Bool {
+ switch behaviour {
+ case .syncWrite, .asyncWrite: return true
+ case .syncRead, .asyncRead: return false
+ }
+ }
+ var isAsync: Bool {
+ switch behaviour {
+ case .asyncRead, .asyncWrite: return true
+ case .syncRead, .syncWrite: return false
+ }
+ }
+
+ init(
+ _ storage: Storage?,
+ _ file: String,
+ _ function: String,
+ _ line: Int,
+ _ behaviour: Behaviour
+ ) {
+ self.storage = storage
+ self.file = file
+ self.function = function
+ self.line = line
+ self.behaviour = behaviour
}
- }
-
- init(
- _ storage: Storage?,
- _ file: String,
- _ function: String,
- _ line: Int,
- _ behaviour: Behaviour
- ) {
- self.storage = storage
- self.file = file
- self.function = function
- self.line = line
- self.behaviour = behaviour
}
}
}
@@ -967,7 +974,7 @@ private extension Storage {
result?.timer = nil
let action: String = (info.isWrite ? "write" : "read")
- Log.warn(.storage, "Slow \(action) taking longer than \(Storage.slowTransactionThreshold, format: ".2", omitZeroDecimal: true)s - [ \(info.callInfo) ]")
+ Log.warn("[Storage] Slow \(action) taking longer than \(Storage.slowTransactionThreshold, format: ".2", omitZeroDecimal: true)s - [ \(info.callInfo) ]")
result?.wasSlowTransaction = true
}
result.timer?.resume()
@@ -983,7 +990,7 @@ private extension Storage {
let end: CFTimeInterval = CACurrentMediaTime()
let action: String = (info.isWrite ? "write" : "read")
- Log.warn(.storage, "Slow \(action) completed after \(end - start, format: ".2", omitZeroDecimal: true)s - [ \(info.callInfo) ]")
+ Log.warn("[Storage] Slow \(action) completed after \(end - start, format: ".2", omitZeroDecimal: true)s - [ \(info.callInfo) ]")
}
}
}
diff --git a/SessionUtilitiesKit/JobRunner/JobRunner.swift b/SessionUtilitiesKit/JobRunner/JobRunner.swift
index 55dbd0033..13005027c 100644
--- a/SessionUtilitiesKit/JobRunner/JobRunner.swift
+++ b/SessionUtilitiesKit/JobRunner/JobRunner.swift
@@ -1355,7 +1355,7 @@ public final class JobQueue: Hashable {
// thread and do so by creating a number of background queues to run the jobs on, if this
// function was called on the wrong queue then we need to dispatch to the correct one
guard DispatchQueue.with(key: queueKey, matches: queueContext, using: dependencies) else {
- internalQueue.async { [weak self] in
+ internalQueue.async(using: dependencies) { [weak self] in
self?.start(forceWhenAlreadyRunning: forceWhenAlreadyRunning)
}
return