diff --git a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt index 56c88d46277..a25bd87acf8 100644 --- a/android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt +++ b/android/app/src/main/java/chat/rocket/reactnative/MainApplication.kt @@ -66,15 +66,18 @@ open class MainApplication : Application(), ReactApplication { override fun onCreate() { super.onCreate() + Bugsnag.start(this) - + + migrateLegacyExperimentalDatabases() + // Initialize MMKV encryption - reads existing key or generates new one // Must run before React Native starts to avoid race conditions MMKVKeyManager.initialize(this) // Load the native entry point for the New Architecture loadReactNative(this) - + ApplicationLifecycleDispatcher.onApplicationCreate(this) } @@ -82,4 +85,30 @@ open class MainApplication : Application(), ReactApplication { super.onConfigurationChanged(newConfig) ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) } + + // Renames <=4.72.0 `-experimental.db.db` files. + private fun migrateLegacyExperimentalDatabases() { + val dir = java.io.File(applicationInfo.dataDir) + val legacySuffix = "-experimental.db.db" + val files = dir.listFiles() ?: return + for (legacy in files) { + if (!legacy.isFile || !legacy.name.endsWith(legacySuffix)) { + continue + } + val baseName = legacy.name.removeSuffix(legacySuffix) + val target = java.io.File(dir, "$baseName.db.db") + if (target.exists() && target.length() > 0) { + continue + } + for (suffix in listOf("-wal", "-shm", "-journal", "")) { + val from = java.io.File(dir, legacy.name + suffix) + val to = java.io.File(dir, target.name + suffix) + to.delete() + if (from.exists() && !from.renameTo(to)) { + // Starting with a fresh DB at `target` would strand the legacy data, since later launches skip a non-empty target. + throw IllegalStateException("Failed to migrate ${from.name} to ${to.name}") + } + } + } + } } diff --git a/ios/AppDelegate.swift b/ios/AppDelegate.swift index 2699b2c7fba..bdfe963330c 100644 --- a/ios/AppDelegate.swift +++ b/ios/AppDelegate.swift @@ -22,9 +22,10 @@ public class AppDelegate: ExpoAppDelegate { // This reads existing encryption key or generates a new one for fresh installs // Must run before Firebase, Bugsnag, and React Native start MMKVKeyManager.initialize() - + FirebaseApp.configure() Bugsnag.start() + migrateLegacyExperimentalDatabases() ReplyNotification.configure() if !VoipRegion.isChina() { VoipService.voipRegistration() @@ -69,6 +70,39 @@ public class AppDelegate: ExpoAppDelegate { return result } + // Renames <=4.72.0 `-experimental.db` files; a 0-byte target is one NotificationService created. + private func migrateLegacyExperimentalDatabases() { + let fileManager = FileManager.default + guard let suite = Bundle.main.object(forInfoDictionaryKey: "AppGroupIdentifier") as? String, + let containerURL = fileManager.containerURL(forSecurityApplicationGroupIdentifier: suite), + let files = try? fileManager.contentsOfDirectory(at: containerURL, includingPropertiesForKeys: nil) + else { + return + } + let legacySuffix = "-experimental.db" + for legacy in files { + let name = legacy.lastPathComponent + guard name.hasSuffix(legacySuffix) else { continue } + let target = containerURL.appendingPathComponent(String(name.dropLast(legacySuffix.count)) + ".db") + if let targetSize = (try? fileManager.attributesOfItem(atPath: target.path))?[.size] as? Int, targetSize > 0 { + continue + } + for suffix in ["-wal", "-shm", "-journal", ""] { + let from = URL(fileURLWithPath: legacy.path + suffix) + let to = URL(fileURLWithPath: target.path + suffix) + try? fileManager.removeItem(at: to) + guard fileManager.fileExists(atPath: from.path) else { continue } + do { + try fileManager.moveItem(at: from, to: to) + } catch { + // Starting with a fresh DB at `target` would strand the legacy data, since later launches skip a non-empty target. + Bugsnag.notifyError(error) + fatalError("Failed to migrate \(from.lastPathComponent) to \(to.lastPathComponent): \(error)") + } + } + } + } + // Linking API public override func application( _ app: UIApplication,