Fixed a number of bugs

• Added a 'requireAllRequestsSucceed' flag to the ConfigurationSyncJob (so it'll report a failure if any individual request fails)
• Updated the 'unknownMessage' error to include some info about the data contained in the protobuf
• Fixed an issue where the logger wasn't correctly respecting the log level settings
• Fixed an issue where the path status indicator wouldn't default to unknown
• Fixed an issue where the generic database error didn't replace the 'app_name' variable
• Fixed an issue where a group could be partially created due to one of it's configs failing to be stored (we now consider that a failure so the user can try again)
• Fixed an issue where the background pollers could get released before they finish polling
• Fixed an issue where the community poller would only ever fetch the most recent 100 messages (instead of everything since the last poll)
• Fixed an issue where we could incorrectly clear the app badge number in some cases
• Fixed an issue where processing a config message in the BackgroundPoller could result in attempting to fetch from communities after the process completed
• Fixed a crash where a database query could incorrectly be interrupted after it completed if both happened at just the right time
pull/1061/head^2
Morgan Pretty 2 months ago
parent a63e58b96b
commit c51f4fda5e

@ -381,19 +381,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// Ensure we haven't timed out yet // Ensure we haven't timed out yet
guard timer.isCancelled == false else { return } guard timer.isCancelled == false else { return }
// Immediately cancel the timer to prevent the timeout being triggered /// Update the app badge in case the unread count changed (but only if the database is valid and
timer.cancel() /// not suspended)
if
// Update the unread count badge dependencies[singleton: .storage].isValid &&
let unreadCount: Int = dependencies[singleton: .storage] !dependencies[singleton: .storage].isSuspended
.read { db in try Interaction.fetchAppBadgeUnreadCount(db, using: dependencies) } {
.defaulting(to: 0) let unreadCount: Int = dependencies[singleton: .storage]
.read { db in try Interaction.fetchAppBadgeUnreadCount(db, using: dependencies) }
DispatchQueue.main.async(using: dependencies) { .defaulting(to: 0)
UIApplication.shared.applicationIconBadgeNumber = unreadCount UIApplication.shared.applicationIconBadgeNumber = unreadCount
} }
// If we are still running in the background then suspend the network & database
if dependencies[singleton: .appContext].isInBackground { if dependencies[singleton: .appContext].isInBackground {
dependencies.mutate(cache: .libSessionNetwork) { $0.suspendNetworkAccess() } dependencies.mutate(cache: .libSessionNetwork) { $0.suspendNetworkAccess() }
dependencies[singleton: .storage].suspendDatabaseAccess() dependencies[singleton: .storage].suspendDatabaseAccess()
@ -784,7 +783,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
/// On application startup the `Storage.read` can be slightly slow while GRDB spins up it's database /// On application startup the `Storage.read` can be slightly slow while GRDB spins up it's database
/// read pools (up to a few seconds), since this read is blocking we want to dispatch it to run async to ensure /// read pools (up to a few seconds), since this read is blocking we want to dispatch it to run async to ensure
/// we don't block user interaction while it's running /// we don't block user interaction while it's running
///
/// **Note:** Only do this if the database is still valid and not suspended (otherwise we will just reset the badge
/// number incorrectly)
DispatchQueue.global(qos: .default).async { DispatchQueue.global(qos: .default).async {
guard
dependencies[singleton: .storage].isValid &&
!dependencies[singleton: .storage].isSuspended
else { return }
let unreadCount: Int = dependencies[singleton: .storage] let unreadCount: Int = dependencies[singleton: .storage]
.read { db in try Interaction.fetchAppBadgeUnreadCount(db, using: dependencies) } .read { db in try Interaction.fetchAppBadgeUnreadCount(db, using: dependencies) }
.defaulting(to: 0) .defaulting(to: 0)

@ -354,7 +354,7 @@ public extension Message {
return variant.messageType.fromProto(proto, sender: sender, using: dependencies) return variant.messageType.fromProto(proto, sender: sender, using: dependencies)
} }
return try decodedMessage ?? { throw MessageReceiverError.unknownMessage }() return try decodedMessage ?? { throw MessageReceiverError.unknownMessage(proto) }()
} }
static func requiresExistingConversation(message: Message, threadVariant: SessionThread.Variant) -> Bool { static func requiresExistingConversation(message: Message, threadVariant: SessionThread.Variant) -> Bool {

@ -4,13 +4,13 @@
import Foundation import Foundation
public enum MessageReceiverError: LocalizedError { public enum MessageReceiverError: Error, CustomStringConvertible {
case duplicateMessage case duplicateMessage
case duplicateMessageNewSnode case duplicateMessageNewSnode
case duplicateControlMessage case duplicateControlMessage
case invalidMessage case invalidMessage
case invalidSender case invalidSender
case unknownMessage case unknownMessage(SNProtoContent?)
case unknownEnvelopeType case unknownEnvelopeType
case noUserX25519KeyPair case noUserX25519KeyPair
case noUserED25519KeyPair case noUserED25519KeyPair
@ -55,14 +55,49 @@ public enum MessageReceiverError: LocalizedError {
} }
} }
public var errorDescription: String? { public var description: String {
switch self { switch self {
case .duplicateMessage: return "Duplicate message." case .duplicateMessage: return "Duplicate message."
case .duplicateMessageNewSnode: return "Duplicate message from different service node." case .duplicateMessageNewSnode: return "Duplicate message from different service node."
case .duplicateControlMessage: return "Duplicate control message." case .duplicateControlMessage: return "Duplicate control message."
case .invalidMessage: return "Invalid message." case .invalidMessage: return "Invalid message."
case .invalidSender: return "Invalid sender." case .invalidSender: return "Invalid sender."
case .unknownMessage: return "Unknown message type." case .unknownMessage(let content):
switch content {
case .none: return "Unknown message type (no content)."
case .some(let content):
let protoInfo: [(String, Bool)] = [
("hasDataMessage", (content.dataMessage != nil)),
("hasProfile", (content.dataMessage?.profile != nil)),
("hasBody", (content.dataMessage?.hasBody == true)),
("hasAttachments", (content.dataMessage?.attachments.isEmpty == false)),
("hasReaction", (content.dataMessage?.reaction != nil)),
("hasQuote", (content.dataMessage?.quote != nil)),
("hasLinkPreview", (content.dataMessage?.preview != nil)),
("hasOpenGroupInvitation", (content.dataMessage?.openGroupInvitation != nil)),
("hasLegacyGroupControlMessage", (content.dataMessage?.closedGroupControlMessage != nil)),
("hasGroupV2ControlMessage", (content.dataMessage?.groupUpdateMessage != nil)),
("hasTimestamp", (content.dataMessage?.hasTimestamp == true)),
("hasSyncTarget", (content.dataMessage?.hasSyncTarget == true)),
("hasBlocksCommunityMessageRequests", (content.dataMessage?.hasBlocksCommunityMessageRequests == true)),
("hasCallMessage", (content.callMessage != nil)),
("hasReceiptMessage", (content.receiptMessage != nil)),
("hasTypingMessage", (content.typingMessage != nil)),
("hasDataExtractionMessage", (content.dataExtractionNotification != nil)),
("hasUnsendRequest", (content.unsendRequest != nil)),
("hasMessageRequestResponse", (content.messageRequestResponse != nil)),
("hasExpirationTimer", (content.hasExpirationTimer == true)),
("hasExpirationType", (content.hasExpirationType == true)),
("hasSigTimestamp", (content.hasSigTimestamp == true))
]
let protoInfoString: String = protoInfo
.filter { _, val in val }
.map { name, _ in name }
.joined(separator: ", ")
return "Unknown message type (\(protoInfoString))."
}
case .unknownEnvelopeType: return "Unknown envelope type." case .unknownEnvelopeType: return "Unknown envelope type."
case .noUserX25519KeyPair: return "Couldn't find user X25519 key pair." case .noUserX25519KeyPair: return "Couldn't find user X25519 key pair."
case .noUserED25519KeyPair: return "Couldn't find user ED25519 key pair." case .noUserED25519KeyPair: return "Couldn't find user ED25519 key pair."

@ -234,7 +234,7 @@ public enum MessageReceiver {
case .all, .unknown: case .all, .unknown:
Log.warn(.messageReceiver, "Couldn't process message due to invalid namespace.") Log.warn(.messageReceiver, "Couldn't process message due to invalid namespace.")
throw MessageReceiverError.unknownMessage throw MessageReceiverError.unknownMessage(nil)
} }
} }
@ -440,7 +440,7 @@ public enum MessageReceiver {
using: dependencies using: dependencies
) )
default: throw MessageReceiverError.unknownMessage default: throw MessageReceiverError.unknownMessage(proto)
} }
// Perform any required post-handling logic // Perform any required post-handling logic

@ -89,9 +89,17 @@ public class NSENotificationPresenter: NotificationsManagerType {
notificationContent.sound = thread.notificationSound notificationContent.sound = thread.notificationSound
.defaulting(to: db[.defaultNotificationSound] ?? Preferences.Sound.defaultNotificationSound) .defaulting(to: db[.defaultNotificationSound] ?? Preferences.Sound.defaultNotificationSound)
.notificationSound(isQuiet: false) .notificationSound(isQuiet: false)
notificationContent.badge = (try? Interaction.fetchAppBadgeUnreadCount(db, using: dependencies))
.map { NSNumber(value: $0) } /// Update the app badge in case the unread count changed (but only if the database is valid and
.defaulting(to: NSNumber(value: 0)) /// not suspended)
if
dependencies[singleton: .storage].isValid &&
!dependencies[singleton: .storage].isSuspended
{
notificationContent.badge = (try? Interaction.fetchAppBadgeUnreadCount(db, using: dependencies))
.map { NSNumber(value: $0) }
.defaulting(to: NSNumber(value: 0))
}
// Title & body // Title & body
let previewType: Preferences.NotificationPreviewType = db[.preferencesNotificationPreviewType] let previewType: Preferences.NotificationPreviewType = db[.preferencesNotificationPreviewType]
@ -198,9 +206,17 @@ public class NSENotificationPresenter: NotificationsManagerType {
notificationContent.sound = thread.notificationSound notificationContent.sound = thread.notificationSound
.defaulting(to: db[.defaultNotificationSound] ?? Preferences.Sound.defaultNotificationSound) .defaulting(to: db[.defaultNotificationSound] ?? Preferences.Sound.defaultNotificationSound)
.notificationSound(isQuiet: false) .notificationSound(isQuiet: false)
notificationContent.badge = (try? Interaction.fetchAppBadgeUnreadCount(db, using: dependencies))
.map { NSNumber(value: $0) } /// Update the app badge in case the unread count changed (but only if the database is valid and
.defaulting(to: NSNumber(value: 0)) /// not suspended)
if
dependencies[singleton: .storage].isValid &&
!dependencies[singleton: .storage].isSuspended
{
notificationContent.badge = (try? Interaction.fetchAppBadgeUnreadCount(db, using: dependencies))
.map { NSNumber(value: $0) }
.defaulting(to: NSNumber(value: 0))
}
notificationContent.title = Constants.app_name notificationContent.title = Constants.app_name
notificationContent.body = "" notificationContent.body = ""

@ -478,10 +478,18 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
switch resolution { switch resolution {
case .ignoreDueToMainAppRunning: break case .ignoreDueToMainAppRunning: break
default: default:
silentContent.badge = dependencies[singleton: .storage] /// Update the app badge in case the unread count changed (but only if the database is valid and
.read { [dependencies] db in try Interaction.fetchAppBadgeUnreadCount(db, using: dependencies) } /// not suspended)
.map { NSNumber(value: $0) } if
.defaulting(to: NSNumber(value: 0)) dependencies[singleton: .storage].isValid &&
!dependencies[singleton: .storage].isSuspended
{
silentContent.badge = dependencies[singleton: .storage]
.read { [dependencies] db in try Interaction.fetchAppBadgeUnreadCount(db, using: dependencies) }
.map { NSNumber(value: $0) }
.defaulting(to: NSNumber(value: 0))
}
dependencies[singleton: .storage].suspendDatabaseAccess() dependencies[singleton: .storage].suspendDatabaseAccess()
} }
@ -538,9 +546,17 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
let notificationContent = UNMutableNotificationContent() let notificationContent = UNMutableNotificationContent()
notificationContent.userInfo = [ NotificationServiceExtension.isFromRemoteKey : true ] notificationContent.userInfo = [ NotificationServiceExtension.isFromRemoteKey : true ]
notificationContent.title = Constants.app_name notificationContent.title = Constants.app_name
notificationContent.badge = (try? Interaction.fetchAppBadgeUnreadCount(db, using: dependencies))
.map { NSNumber(value: $0) } /// Update the app badge in case the unread count changed (but only if the database is valid and
.defaulting(to: NSNumber(value: 0)) /// not suspended)
if
dependencies[singleton: .storage].isValid &&
!dependencies[singleton: .storage].isSuspended
{
notificationContent.badge = (try? Interaction.fetchAppBadgeUnreadCount(db, using: dependencies))
.map { NSNumber(value: $0) }
.defaulting(to: NSNumber(value: 0))
}
if let sender: String = callMessage.sender { if let sender: String = callMessage.sender {
let senderDisplayName: String = Profile.displayName(db, id: sender, threadVariant: .contact, using: dependencies) let senderDisplayName: String = Profile.displayName(db, id: sender, threadVariant: .contact, using: dependencies)

@ -7,7 +7,7 @@ public class SnodeRecursiveResponse<T: SnodeSwarmItem>: SnodeResponse {
case swarm case swarm
} }
internal let swarm: [String: T] public let swarm: [String: T]
// MARK: - Initialization // MARK: - Initialization

@ -150,6 +150,7 @@ extension Network.BatchSubResponse: Decodable {
// MARK: - ErasedBatchSubResponse // MARK: - ErasedBatchSubResponse
public protocol ErasedBatchSubResponse: ResponseInfoType { public protocol ErasedBatchSubResponse: ResponseInfoType {
var code: Int { get }
var erasedBody: Any? { get } var erasedBody: Any? { get }
var failedToParseBody: Bool { get } var failedToParseBody: Bool { get }
} }

@ -578,7 +578,10 @@ open class Storage {
switch error { switch error {
case DatabaseError.SQLITE_ABORT, DatabaseError.SQLITE_INTERRUPT, DatabaseError.SQLITE_ERROR: case DatabaseError.SQLITE_ABORT, DatabaseError.SQLITE_INTERRUPT, DatabaseError.SQLITE_ERROR:
let message: String = ((error as? DatabaseError)?.message ?? "Unknown") let message: String = ((error as? DatabaseError)?.message ?? "Unknown")
Log.error(.storage, "Database \(action) failed due to error: \(message) - [ \(info.callInfo) ]") Log.error(.storage, "Database \(action) failed due to error: \(message)")
case StorageError.databaseInvalid:
Log.error(.storage, "Database \(action) failed as the database is invalid.")
case StorageError.databaseInvalid: case StorageError.databaseInvalid:
Log.error(.storage, "Database \(action) failed as the database is invalid - [ \(info.callInfo) ]") Log.error(.storage, "Database \(action) failed as the database is invalid - [ \(info.callInfo) ]")

Loading…
Cancel
Save