mirror of
https://github.com/luxfi/safe-wallet.git
synced 2026-10-30 15:19:21 +00:00
* fix(mobile): display notification even when app in quit state * fix(mobile): improve performance by syncing the store only when necessary We were subscribing to any kind of store update, but were interested only in the contacts and chain data. By using the store middleware we make sure that we sync the state only when those slices change. * fix(mobile): require cycle * fix(mobile): deprecated messaging usage * fix(mobile): failing test due to new middleware
136 lines
5.7 KiB
Swift
136 lines
5.7 KiB
Swift
import UserNotifications
|
|
import RNNotifeeCore
|
|
import MMKV
|
|
import SwiftCryptoTokenFormatter
|
|
import BigInt
|
|
|
|
struct ChainInfo: Codable {
|
|
let name: String
|
|
let symbol: String
|
|
let decimals: Int
|
|
}
|
|
|
|
struct ExtensionStore: Codable {
|
|
let chains: [String: ChainInfo]
|
|
let contacts: [String: String]
|
|
}
|
|
|
|
func loadExtensionStore() -> ExtensionStore? {
|
|
guard let groupDir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "[NOTIFICATION_APP_GROUP_IDENTIFIER]")?.path else {
|
|
NSLog("[NotifeeDebug] Failed to get app group directory")
|
|
return nil
|
|
}
|
|
guard let kv = MMKV(mmapID: "extension", cryptKey: nil, rootPath: groupDir, mode: .multiProcess, expectedCapacity: 0) else {
|
|
NSLog("[NotifeeDebug] Failed to open MMKV")
|
|
return nil
|
|
}
|
|
guard let json = kv.string(forKey: "notification-extension-data") else {
|
|
NSLog("[NotifeeDebug] No data found for notification-extension-data key")
|
|
return nil
|
|
}
|
|
guard let data = json.data(using: String.Encoding.utf8) else {
|
|
NSLog("[NotifeeDebug] Failed to convert json to data")
|
|
return nil
|
|
}
|
|
return try? JSONDecoder().decode(ExtensionStore.self, from: data)
|
|
}
|
|
|
|
|
|
func parseNotification(userInfo: [AnyHashable: Any], store: ExtensionStore) -> (String, String)? {
|
|
NSLog("[NotifeeDebug] Parsing notification with userInfo: \(userInfo)")
|
|
|
|
guard let type = userInfo["type"] as? String else {
|
|
NSLog("[NotifeeDebug] No type found in notification")
|
|
return nil
|
|
}
|
|
NSLog("[NotifeeDebug] Notification type: \(type)")
|
|
|
|
let chainId = userInfo["chainId"] as? String
|
|
let address = userInfo["address"] as? String
|
|
|
|
NSLog("[NotifeeDebug] ChainId: \(chainId ?? "nil"), Address: \(address ?? "nil")")
|
|
|
|
let chainInfo = chainId.flatMap { store.chains[$0] }
|
|
let chainName = chainInfo?.name ?? (chainId != nil ? "Chain Id \(chainId!)" : "")
|
|
let safeName = address.flatMap { store.contacts[$0] } ?? (address ?? "")
|
|
|
|
NSLog("[NotifeeDebug] Resolved chainName: \(chainName), safeName: \(safeName)")
|
|
|
|
switch type {
|
|
case "INCOMING_ETHER":
|
|
// Use chain symbol if available, otherwise fall back to userInfo or default
|
|
let symbol = chainInfo?.symbol ?? userInfo["symbol"] as? String ?? "ETH"
|
|
let formatter = TokenFormatter()
|
|
let value = userInfo["value"] as? String ?? ""
|
|
// Use chain decimals if available, otherwise fall back to userInfo or default
|
|
let decimals = chainInfo?.decimals ?? Int(userInfo["decimals"] as? String ?? "18") ?? 18
|
|
let amount = formatter.string(
|
|
from: BigDecimal(BigInt(value) ?? BigInt(0), decimals),
|
|
decimalSeparator: Locale.autoupdatingCurrent.decimalSeparator ?? ".",
|
|
thousandSeparator: Locale.autoupdatingCurrent.groupingSeparator ?? ",")
|
|
|
|
return ("Incoming \(symbol) (\(chainName))", "\(safeName): \(amount) \(symbol) received")
|
|
case "INCOMING_TOKEN":
|
|
return ("Incoming token (\(chainName))", "\(safeName): tokens received")
|
|
case "EXECUTED_MULTISIG_TRANSACTION":
|
|
let status = (userInfo["failed"] as? String) == "true" ? "failed" : "successful"
|
|
return ("Transaction \(status) (\(chainName))", "\(safeName): Transaction \(status)")
|
|
case "CONFIRMATION_REQUEST":
|
|
return ("Confirmation required (\(chainName))", "\(safeName): A transaction requires your confirmation!")
|
|
default:
|
|
return nil
|
|
}
|
|
}
|
|
|
|
class NotificationService: UNNotificationServiceExtension {
|
|
let appGroup = "[NOTIFICATION_APP_GROUP_IDENTIFIER]"
|
|
var contentHandler: ((UNNotificationContent) -> Void)?
|
|
var bestAttemptContent: UNMutableNotificationContent?
|
|
|
|
override init() {
|
|
super.init()
|
|
if let groupDir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)?.path {
|
|
MMKV.initialize(rootDir: groupDir)
|
|
} else {
|
|
NSLog("[NotifeeDebug] Failed to initialize MMKV: couldn't get app group directory")
|
|
}
|
|
}
|
|
|
|
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
|
NSLog("[NotifeeDebug] Received notification request with id: \(request.identifier)")
|
|
self.contentHandler = contentHandler
|
|
self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
|
|
|
if let mutable = self.bestAttemptContent {
|
|
NSLog("[NotifeeDebug] Successfully created mutable content")
|
|
|
|
if let store = loadExtensionStore() {
|
|
NSLog("[NotifeeDebug] Successfully loaded extension store")
|
|
|
|
if let parsed = parseNotification(userInfo: request.content.userInfo, store: store) {
|
|
NSLog("[NotifeeDebug] Successfully parsed notification: title=\(parsed.0), body=\(parsed.1)")
|
|
mutable.title = parsed.0
|
|
mutable.body = parsed.1
|
|
mutable.badge = 1
|
|
} else {
|
|
NSLog("[NotifeeDebug] Failed to parse notification")
|
|
}
|
|
} else {
|
|
NSLog("[NotifeeDebug] Failed to load extension store")
|
|
}
|
|
|
|
NotifeeExtensionHelper.populateNotificationContent(request, with: mutable, withContentHandler: contentHandler)
|
|
} else {
|
|
NSLog("[NotifeeDebug] Failed to create mutable content")
|
|
contentHandler(request.content)
|
|
}
|
|
}
|
|
|
|
override func serviceExtensionTimeWillExpire() {
|
|
NSLog("[NotifeeDebug] Service extension time will expire")
|
|
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
|
|
contentHandler(bestAttemptContent)
|
|
}
|
|
}
|
|
}
|