mirror of https://github.com/oxen-io/session-ios
Fixed a number of bugs found in the internal release
• Moved the 'getSwarm' behaviour into a distinct job to prevent duplicate API calls • Updated to the latest libSession (fix libQuic crash) • Updated the JobRunner to support the `runOnceTransient` behaviour and be able to run transient jobs in the app extensions • Reworked the extension file logging to be written directly to the file in a single operation rather than line-by-line via the logger • Fixed a bug where community invites has the wrong author • Fixed a bug where the title on the disappearing messages settings screen was clipping vertically • Fixed a bug where tapping on the disappearing messages setting subtitle could incorrectly appear in read-only state for admins • Fixed a log which contained notification content • Tweaks to extension logging logicpull/960/head
parent
2cffda17bc
commit
a3188ebea4
@ -1 +1 @@
|
||||
Subproject commit 1c4667ba0c56c924d4e957743d1324be2c899040
|
||||
Subproject commit 7651967104845db16e6a58f70635c01f7f4c2033
|
@ -1,18 +1,20 @@
|
||||
// Copyright © 2023 Rangeproof Pty Ltd. All rights reserved.
|
||||
//
|
||||
// stringlint:disable
|
||||
|
||||
import Foundation
|
||||
import SessionMessagingKit
|
||||
|
||||
enum NotificationError: LocalizedError {
|
||||
enum NotificationError: Error, CustomStringConvertible {
|
||||
case processing(PushNotificationAPI.ProcessResult)
|
||||
case messageProcessing
|
||||
case messageHandling(MessageReceiverError)
|
||||
|
||||
public var errorDescription: String? {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .processing(let result): return "Failed to process notification (\(result))"
|
||||
case .messageProcessing: return "Failed to process message"
|
||||
case .messageHandling(let error): return "Failed to handle message (\(error))"
|
||||
case .processing(let result): return "Failed to process notification (\(result)) (NotificationError.processing)."
|
||||
case .messageProcessing: return "Failed to process message (NotificationError.messageProcessing)."
|
||||
case .messageHandling(let error): return "Failed to handle message (\(error)) (NotificationError.messageHandling)."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,133 @@
|
||||
// Copyright © 2024 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import GRDB
|
||||
import SessionUtilitiesKit
|
||||
|
||||
public enum GetSwarmJob: JobExecutor {
|
||||
public static let maxFailureCount: Int = 0
|
||||
public static let requiresThreadId: Bool = false
|
||||
public static let requiresInteractionId: Bool = false
|
||||
|
||||
/// The minimum number of snodes in a swarm.
|
||||
private static let minSwarmSnodeCount: Int = 3
|
||||
|
||||
public static func run(
|
||||
_ job: Job,
|
||||
queue: DispatchQueue,
|
||||
success: @escaping (Job, Bool, Dependencies) -> (),
|
||||
failure: @escaping (Job, Error?, Bool, Dependencies) -> (),
|
||||
deferred: @escaping (Job, Dependencies) -> (),
|
||||
using dependencies: Dependencies
|
||||
) {
|
||||
guard
|
||||
let detailsData: Data = job.details,
|
||||
let details: Details = try? JSONDecoder().decode(Details.self, from: detailsData)
|
||||
else {
|
||||
SNLog("[GetSwarmJob] Failing due to missing details.")
|
||||
return failure(job, JobRunnerError.missingRequiredDetails, true, dependencies)
|
||||
}
|
||||
|
||||
SNLog("[GetSwarmJob] Retrieving swarm for \(details.swarmPublicKey).")
|
||||
return SnodeAPI
|
||||
.getSwarm(for: details.swarmPublicKey, using: dependencies)
|
||||
.subscribe(on: queue, using: dependencies)
|
||||
.receive(on: queue, using: dependencies)
|
||||
.sinkUntilComplete(
|
||||
receiveCompletion: { result in
|
||||
switch result {
|
||||
case .finished: break
|
||||
case .failure(let error):
|
||||
SNLog("[GetSwarmJob] Failed due to error: \(error)")
|
||||
failure(job, error, false, dependencies)
|
||||
}
|
||||
},
|
||||
receiveValue: { (snodes: Set<Snode>) in
|
||||
// Store the swarm and update the 'loadedSwarms' state so we don't fetch it again from the
|
||||
// database the next time it's used
|
||||
SnodeAPI.setSwarm(to: snodes, for: details.swarmPublicKey)
|
||||
SnodeAPI.loadedSwarms.mutate { $0.insert(details.swarmPublicKey) }
|
||||
|
||||
SNLog("[GetSwarmJob] Complete.")
|
||||
success(job, false, dependencies)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public static func run(
|
||||
for swarmPublicKey: String,
|
||||
using dependencies: Dependencies
|
||||
) -> AnyPublisher<Set<Snode>, Error> {
|
||||
// Try to load the swarm from the database if we haven't already
|
||||
if !SnodeAPI.loadedSwarms.wrappedValue.contains(swarmPublicKey) {
|
||||
let updatedCacheForKey: Set<Snode> = dependencies.storage
|
||||
.read { db in try Snode.fetchSet(db, publicKey: swarmPublicKey) }
|
||||
.defaulting(to: [])
|
||||
|
||||
SnodeAPI.swarmCache.mutate { $0[swarmPublicKey] = updatedCacheForKey }
|
||||
SnodeAPI.loadedSwarms.mutate { $0.insert(swarmPublicKey) }
|
||||
}
|
||||
|
||||
// If we already have a cached version of the swarm which is large enough then use that
|
||||
if let cachedSwarm = SnodeAPI.swarmCache.wrappedValue[swarmPublicKey], cachedSwarm.count >= minSwarmSnodeCount {
|
||||
return Just(cachedSwarm)
|
||||
.setFailureType(to: Error.self)
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
// Otherwise trigger the job
|
||||
return Deferred {
|
||||
Future<Set<Snode>, Error> { resolver in
|
||||
let targetJob: Job? = dependencies.storage.write(using: dependencies) { db in
|
||||
return dependencies.jobRunner.upsert(
|
||||
db,
|
||||
job: Job(
|
||||
variant: .getSwarm,
|
||||
behaviour: .runOnceTransient,
|
||||
shouldBeUnique: true,
|
||||
details: Details(swarmPublicKey: swarmPublicKey)
|
||||
),
|
||||
canStartJob: true,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
|
||||
guard let job: Job = targetJob else {
|
||||
SNLog("[GetSwarmJob] Failed to retrieve existing job or schedule a new one.")
|
||||
return resolver(Result.failure(JobRunnerError.generic))
|
||||
}
|
||||
|
||||
dependencies.jobRunner.afterJob(job) { result in
|
||||
switch result {
|
||||
case .succeeded:
|
||||
guard
|
||||
let cachedSwarm = SnodeAPI.swarmCache.wrappedValue[swarmPublicKey],
|
||||
cachedSwarm.count >= minSwarmSnodeCount
|
||||
else {
|
||||
SNLog("[GetSwarmJob] Failed to find swarm in cache after job.")
|
||||
return resolver(Result.failure(JobRunnerError.generic))
|
||||
}
|
||||
|
||||
resolver(Result.success(cachedSwarm))
|
||||
|
||||
case .failed(let error, _): resolver(Result.failure(error ?? JobRunnerError.generic))
|
||||
case .deferred, .notFound: resolver(Result.failure(JobRunnerError.generic))
|
||||
}
|
||||
}
|
||||
}
|
||||
}.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - GetSwarmJob.Details
|
||||
|
||||
extension GetSwarmJob {
|
||||
public struct Details: Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case swarmPublicKey
|
||||
}
|
||||
|
||||
fileprivate let swarmPublicKey: String
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue