diff --git a/android/app/build.gradle b/android/app/build.gradle
index e200ccee..11f479be 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -73,6 +73,9 @@ flutter {
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
+ implementation platform('com.google.firebase:firebase-bom:34.9.0')
+ implementation 'com.google.firebase:firebase-messaging'
+ implementation 'androidx.work:work-runtime:2.10.2'
implementation 'com.otaliastudios:transcoder:0.11.0'
implementation 'androidx.core:core-splashscreen:1.0.1'
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 8f648018..76b2cfaf 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -71,6 +71,25 @@
android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.JobInfoSchedulerService"
tools:node="remove">
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/eu/twonly/MainActivity.kt b/android/app/src/main/kotlin/eu/twonly/MainActivity.kt
index 8b680aad..24205927 100644
--- a/android/app/src/main/kotlin/eu/twonly/MainActivity.kt
+++ b/android/app/src/main/kotlin/eu/twonly/MainActivity.kt
@@ -7,6 +7,7 @@ import android.view.KeyEvent.KEYCODE_VOLUME_DOWN
import android.view.KeyEvent.KEYCODE_VOLUME_UP
import io.flutter.embedding.engine.FlutterEngine
import android.content.Context
+import android.content.Intent
import io.crates.keyring.Keyring
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import android.os.Bundle
@@ -16,6 +17,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import io.flutter.plugin.common.MethodChannel
+import eu.twonly.notifications.NotificationTapChannel
class MainActivity : FlutterFragmentActivity() {
private val CHANNEL = "eu.twonly/photo_picker"
@@ -25,6 +27,10 @@ class MainActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
+
+ // Buffer a notification tap before the Flutter engine exists so the
+ // cold-start route is not lost.
+ NotificationTapChannel.handleIntent(intent)
pickMultipleMedia = registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()) { uris ->
if (uris.isNotEmpty()) {
@@ -39,6 +45,12 @@ class MainActivity : FlutterFragmentActivity() {
super.onCreate(savedInstanceState)
}
+ override fun onNewIntent(intent: Intent) {
+ super.onNewIntent(intent)
+ setIntent(intent)
+ NotificationTapChannel.handleIntent(intent)
+ }
+
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (keyCode == KEYCODE_VOLUME_DOWN && eventSink != null) {
eventSink!!.success(true)
@@ -58,6 +70,8 @@ class MainActivity : FlutterFragmentActivity() {
VideoCompressionChannel.configure(flutterEngine, applicationContext)
+ NotificationTapChannel.configure(flutterEngine, applicationContext)
+
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"pickImages" -> {
@@ -88,4 +102,9 @@ class MainActivity : FlutterFragmentActivity() {
}
}
}
+
+ override fun cleanUpFlutterEngine(flutterEngine: FlutterEngine) {
+ NotificationTapChannel.detach()
+ super.cleanUpFlutterEngine(flutterEngine)
+ }
}
diff --git a/android/app/src/main/kotlin/eu/twonly/notifications/NativeNotificationBridge.kt b/android/app/src/main/kotlin/eu/twonly/notifications/NativeNotificationBridge.kt
new file mode 100644
index 00000000..574a4ed5
--- /dev/null
+++ b/android/app/src/main/kotlin/eu/twonly/notifications/NativeNotificationBridge.kt
@@ -0,0 +1,25 @@
+package eu.twonly.notifications
+
+internal object NativeNotificationBridge {
+ init {
+ System.loadLibrary("rust_lib_twonly")
+ }
+
+ @JvmStatic
+ external fun process(
+ databaseDirectory: String,
+ dataDirectory: String,
+ locale: String,
+ deadlineMs: Long,
+ ): String
+
+ @JvmStatic
+ external fun acknowledge(eventIdsJson: String): String
+
+ @JvmStatic
+ external fun storeFcmToken(
+ databaseDirectory: String,
+ dataDirectory: String,
+ token: String,
+ ): String
+}
diff --git a/android/app/src/main/kotlin/eu/twonly/notifications/NativeNotificationModels.kt b/android/app/src/main/kotlin/eu/twonly/notifications/NativeNotificationModels.kt
new file mode 100644
index 00000000..56e5249f
--- /dev/null
+++ b/android/app/src/main/kotlin/eu/twonly/notifications/NativeNotificationModels.kt
@@ -0,0 +1,77 @@
+package eu.twonly.notifications
+
+import org.json.JSONObject
+
+internal data class NativeNotificationPresentation(
+ val title: String,
+ val body: String,
+)
+
+internal data class NativeNotificationAddition(
+ val eventId: String,
+ val notificationId: String,
+ val conversationId: String?,
+ val senderId: Long,
+ val senderName: String,
+ val title: String,
+ val body: String,
+ val createdAt: Long,
+ val avatarPath: String?,
+)
+
+internal data class NativeNotificationBatch(
+ val additions: List,
+ val removals: List,
+ val badgeCount: Long,
+ val completed: Boolean,
+)
+
+internal data class NativeNotificationResponse(
+ val ok: Boolean,
+ val batch: NativeNotificationBatch?,
+ val fallback: NativeNotificationPresentation?,
+) {
+ companion object {
+ fun parse(json: String): NativeNotificationResponse {
+ val root = JSONObject(json)
+ val batchJson = root.optJSONObject("batch")
+ val additions = batchJson?.optJSONArray("additions")?.let { array ->
+ List(array.length()) { index ->
+ val item = array.getJSONObject(index)
+ NativeNotificationAddition(
+ eventId = item.getString("event_id"),
+ notificationId = item.getString("notification_id"),
+ conversationId = item.nullableString("conversation_id"),
+ senderId = item.getLong("sender_id"),
+ senderName = item.getString("sender_name"),
+ title = item.getString("title"),
+ body = item.getString("body"),
+ createdAt = item.getLong("created_at"),
+ avatarPath = item.nullableString("avatar_path"),
+ )
+ }
+ }.orEmpty()
+ val removals = batchJson?.optJSONArray("removals")?.let { array ->
+ List(array.length()) { index -> array.getString(index) }
+ }.orEmpty()
+ val batch = batchJson?.let {
+ NativeNotificationBatch(
+ additions = additions,
+ removals = removals,
+ badgeCount = it.optLong("badge_count"),
+ completed = it.optBoolean("completed"),
+ )
+ }
+ val fallback = root.optJSONObject("fallback")?.let {
+ NativeNotificationPresentation(
+ title = it.getString("title"),
+ body = it.getString("body"),
+ )
+ }
+ return NativeNotificationResponse(root.optBoolean("ok"), batch, fallback)
+ }
+ }
+}
+
+private fun JSONObject.nullableString(key: String): String? =
+ if (isNull(key)) null else optString(key).takeIf(String::isNotEmpty)
diff --git a/android/app/src/main/kotlin/eu/twonly/notifications/NotificationTapChannel.kt b/android/app/src/main/kotlin/eu/twonly/notifications/NotificationTapChannel.kt
new file mode 100644
index 00000000..ba11b8db
--- /dev/null
+++ b/android/app/src/main/kotlin/eu/twonly/notifications/NotificationTapChannel.kt
@@ -0,0 +1,83 @@
+package eu.twonly.notifications
+
+import android.content.Context
+import android.content.Intent
+import androidx.core.app.NotificationManagerCompat
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodChannel
+
+internal fun nativeNotificationId(value: String): Int = value.hashCode() and Int.MAX_VALUE
+
+/**
+ * Forwards taps on natively rendered notifications into Flutter.
+ *
+ * Only the opaque conversation identifier travels across this channel; the
+ * route itself is built in Dart so Kotlin never duplicates Flutter routing.
+ */
+object NotificationTapChannel {
+ private const val CHANNEL = "eu.twonly/notificationTap"
+ const val EXTRA_CONVERSATION_ID = "conversation_id"
+
+ private var channel: MethodChannel? = null
+ private var pendingConversationId: String? = null
+ private var pendingLaunch = false
+
+ fun configure(flutterEngine: FlutterEngine, context: Context) {
+ val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
+ channel.setMethodCallHandler { call, result ->
+ when (call.method) {
+ "consumeInitialNotification" -> {
+ val launched = pendingLaunch
+ val conversationId = pendingConversationId
+ pendingLaunch = false
+ pendingConversationId = null
+ result.success(
+ if (launched) {
+ mapOf(EXTRA_CONVERSATION_ID to conversationId)
+ } else {
+ null
+ },
+ )
+ }
+ "cancelNotifications" -> {
+ val notificationIds = call.argument>("notification_ids").orEmpty()
+ val manager = NotificationManagerCompat.from(context.applicationContext)
+ notificationIds.forEach { manager.cancel(nativeNotificationId(it)) }
+ result.success(null)
+ }
+ else -> result.notImplemented()
+ }
+ }
+ this.channel = channel
+ }
+
+ fun detach() {
+ channel?.setMethodCallHandler(null)
+ channel = null
+ }
+
+ /**
+ * Records the launch intent before Flutter attaches. Called for both the
+ * cold-start intent and every `onNewIntent`; a live channel is notified
+ * immediately, otherwise the tap is buffered for `consumeInitialNotification`.
+ */
+ fun handleIntent(intent: Intent?) {
+ if (intent?.hasExtra(EXTRA_CONVERSATION_ID) != true) return
+ val conversationId =
+ intent.getStringExtra(EXTRA_CONVERSATION_ID)?.takeIf(String::isNotEmpty)
+ // A tap must only route once, even if the activity is recreated with
+ // the same intent after a configuration change.
+ intent.removeExtra(EXTRA_CONVERSATION_ID)
+
+ val channel = this.channel
+ if (channel == null) {
+ pendingLaunch = true
+ pendingConversationId = conversationId
+ return
+ }
+ channel.invokeMethod(
+ "onNotificationTapped",
+ mapOf(EXTRA_CONVERSATION_ID to conversationId),
+ )
+ }
+}
diff --git a/android/app/src/main/kotlin/eu/twonly/notifications/TwonlyFirebaseMessagingService.kt b/android/app/src/main/kotlin/eu/twonly/notifications/TwonlyFirebaseMessagingService.kt
new file mode 100644
index 00000000..c63e6bbd
--- /dev/null
+++ b/android/app/src/main/kotlin/eu/twonly/notifications/TwonlyFirebaseMessagingService.kt
@@ -0,0 +1,44 @@
+package eu.twonly.notifications
+
+import android.util.Log
+import androidx.work.Data
+import androidx.work.ExistingWorkPolicy
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.OutOfQuotaPolicy
+import androidx.work.WorkManager
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+
+class TwonlyFirebaseMessagingService : FirebaseMessagingService() {
+ override fun onMessageReceived(message: RemoteMessage) {
+ if (message.data["kind"] != "message_wakeup" || message.data["version"] != "1") {
+ Log.w(TAG, "Ignoring unsupported opaque FCM payload")
+ return
+ }
+
+ val request = OneTimeWorkRequestBuilder()
+ .setInputData(Data.Builder().putString(INPUT_MESSAGE_ID, message.messageId).build())
+ .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
+ .build()
+ WorkManager.getInstance(applicationContext).enqueueUniqueWork(
+ UNIQUE_WORK,
+ ExistingWorkPolicy.APPEND_OR_REPLACE,
+ request,
+ )
+ }
+
+ override fun onNewToken(token: String) {
+ try {
+ val directory = applicationContext.filesDir.absolutePath
+ NativeNotificationBridge.storeFcmToken(directory, directory, token)
+ } catch (error: Throwable) {
+ Log.e(TAG, "Could not persist refreshed FCM token in Rust", error)
+ }
+ }
+
+ private companion object {
+ const val TAG = "TwonlyFCM"
+ const val UNIQUE_WORK = "twonly-native-notification-drain"
+ const val INPUT_MESSAGE_ID = "fcm_message_id"
+ }
+}
diff --git a/android/app/src/main/kotlin/eu/twonly/notifications/TwonlyNotificationWorker.kt b/android/app/src/main/kotlin/eu/twonly/notifications/TwonlyNotificationWorker.kt
new file mode 100644
index 00000000..dc5f94d6
--- /dev/null
+++ b/android/app/src/main/kotlin/eu/twonly/notifications/TwonlyNotificationWorker.kt
@@ -0,0 +1,156 @@
+package eu.twonly.notifications
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.os.Build
+import android.util.Log
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.app.Person
+import androidx.core.graphics.drawable.IconCompat
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+import eu.twonly.MainActivity
+import eu.twonly.R
+import java.util.Locale
+import org.json.JSONArray
+
+class TwonlyNotificationWorker(
+ appContext: Context,
+ params: WorkerParameters,
+) : Worker(appContext, params) {
+ override fun doWork(): Result {
+ return try {
+ val directory = applicationContext.filesDir.absolutePath
+ val response = NativeNotificationResponse.parse(
+ NativeNotificationBridge.process(
+ directory,
+ directory,
+ Locale.getDefault().toLanguageTag(),
+ RUST_DEADLINE_MS,
+ ),
+ )
+ ensureChannel()
+ val batch = response.batch
+ if (!response.ok || batch == null) {
+ // Rust could not reach the mailbox. Show the generic alert so a
+ // high-priority wake-up still produces a notification, and retry.
+ response.fallback?.let(::showFallback)
+ return if (runAttemptCount < MAX_RETRIES) Result.retry() else Result.success()
+ }
+
+ val manager = NotificationManagerCompat.from(applicationContext)
+ batch.removals.forEach { manager.cancel(nativeNotificationId(it)) }
+ val delivered = batch.additions.filter { addition ->
+ showAddition(manager, addition)
+ }.map(NativeNotificationAddition::eventId)
+ if (delivered.isNotEmpty()) {
+ NativeNotificationBridge.acknowledge(JSONArray(delivered).toString())
+ // Real notifications supersede a placeholder from an earlier attempt.
+ manager.cancel(FALLBACK_ID)
+ }
+
+ // An empty batch is the normal outcome of a duplicate wake-up or of
+ // traffic that is not user visible, so it must not retry. Only an
+ // undrained mailbox is worth another attempt.
+ if (!batch.completed && runAttemptCount < MAX_RETRIES) {
+ Result.retry()
+ } else {
+ Result.success()
+ }
+ } catch (error: Throwable) {
+ Log.e(TAG, "Native notification processing failed", error)
+ if (runAttemptCount < MAX_RETRIES) Result.retry() else Result.failure()
+ }
+ }
+
+ private fun showAddition(
+ manager: NotificationManagerCompat,
+ addition: NativeNotificationAddition,
+ ): Boolean {
+ val avatar = addition.avatarPath
+ ?.let(BitmapFactory::decodeFile)
+ ?.let(IconCompat::createWithBitmap)
+ val sender = Person.Builder()
+ .setName(addition.senderName)
+ .setKey(addition.senderId.toString())
+ .setIcon(avatar)
+ .build()
+ val user = Person.Builder().setName(applicationLabel()).setKey("twonly-user").build()
+ val style = NotificationCompat.MessagingStyle(user)
+ .addMessage(addition.body, addition.createdAt * 1_000, sender)
+ val intent = Intent(applicationContext, MainActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
+ // Only opaque identifiers cross into the activity; Dart owns routing.
+ putExtra(
+ NotificationTapChannel.EXTRA_CONVERSATION_ID,
+ addition.conversationId.orEmpty(),
+ )
+ }
+ val pendingIntent = PendingIntent.getActivity(
+ applicationContext,
+ nativeNotificationId(addition.notificationId),
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
+ )
+ val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_launcher_foreground)
+ .setContentTitle(addition.title)
+ .setContentText(addition.body)
+ .setStyle(style)
+ .setCategory(NotificationCompat.CATEGORY_MESSAGE)
+ .setAutoCancel(true)
+ .setContentIntent(pendingIntent)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setGroup(addition.conversationId ?: addition.senderId.toString())
+ .build()
+ return try {
+ manager.notify(nativeNotificationId(addition.notificationId), notification)
+ true
+ } catch (error: SecurityException) {
+ Log.w(TAG, "Notification permission is unavailable", error)
+ false
+ }
+ }
+
+ private fun showFallback(presentation: NativeNotificationPresentation) {
+ val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_launcher_foreground)
+ .setContentTitle(presentation.title)
+ .setContentText(presentation.body)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setAutoCancel(true)
+ .build()
+ try {
+ NotificationManagerCompat.from(applicationContext).notify(FALLBACK_ID, notification)
+ } catch (error: SecurityException) {
+ Log.w(TAG, "Notification permission is unavailable", error)
+ }
+ }
+
+ private fun ensureChannel() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
+ val channel = NotificationChannel(
+ CHANNEL_ID,
+ applicationLabel(),
+ NotificationManager.IMPORTANCE_HIGH,
+ )
+ applicationContext.getSystemService(NotificationManager::class.java)
+ .createNotificationChannel(channel)
+ }
+
+ private fun applicationLabel(): String =
+ applicationContext.applicationInfo.loadLabel(applicationContext.packageManager).toString()
+
+ private companion object {
+ const val TAG = "TwonlyNotification"
+ const val CHANNEL_ID = "twonly_messages_v2"
+ const val FALLBACK_ID = 0x74776F
+ const val MAX_RETRIES = 2
+ const val RUST_DEADLINE_MS = 25_000L
+ }
+}
diff --git a/ios/NotificationService/NotificationService.entitlements b/ios/NotificationService/NotificationService.entitlements
index 78e4d59c..e32e0256 100644
--- a/ios/NotificationService/NotificationService.entitlements
+++ b/ios/NotificationService/NotificationService.entitlements
@@ -2,6 +2,14 @@
+ com.apple.developer.usernotifications.communication
+
+ com.apple.developer.usernotifications.filtering
+
+ com.apple.security.application-groups
+
+ group.eu.twonly.runtime
+
keychain-access-groups
$(AppIdentifierPrefix)eu.twonly.shared
diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift
index ebf7b038..aaf6976b 100644
--- a/ios/NotificationService/NotificationService.swift
+++ b/ios/NotificationService/NotificationService.swift
@@ -1,326 +1,273 @@
-import CryptoKit
import Foundation
-import Security
+import Intents
import UserNotifications
+import rust_lib_twonly
-class NotificationService: UNNotificationServiceExtension {
-
- var contentHandler: ((UNNotificationContent) -> Void)?
- var bestAttemptContent: UNMutableNotificationContent?
-
- override func didReceive(
- _ request: UNNotificationRequest,
- withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
- ) {
- self.contentHandler = contentHandler
- bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
-
- // Store the current timestamp in Keychain for iOS FCM messaging tracking
- let nowMs = String(format: "%.0f", Date().timeIntervalSince1970 * 1000)
- writeToKeychain(key: "last_fcm_message_timestamp", value: nowMs)
- NSLog("Received APNs push notification, updated last_fcm_message_timestamp to \(nowMs)")
-
- if let bestAttemptContent = bestAttemptContent {
-
- guard bestAttemptContent.userInfo as? [String: Any] != nil,
- let push_data = bestAttemptContent.userInfo["push_data"] as? String
- else {
- return contentHandler(bestAttemptContent)
- }
-
- let data = getPushNotificationData(pushData: push_data)
-
- if data != nil {
- if data!.title == "blocked" {
- NSLog("Block message because user is blocked!")
- // https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.usernotifications.filtering
- return contentHandler(UNNotificationContent())
- }
- bestAttemptContent.title = data!.title
- bestAttemptContent.body = data!.body
- bestAttemptContent.threadIdentifier = String(format: "%d", data!.notificationId)
- } else {
- NSLog("Could not decrypt message. Show default.")
- bestAttemptContent.title = "\(bestAttemptContent.title)"
- }
-
- contentHandler(bestAttemptContent)
- }
- }
-
- override func serviceExtensionTimeWillExpire() {
- // Called just before the extension will be terminated by the system.
- // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
- if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
- contentHandler(bestAttemptContent)
- }
- }
+private let runtimeAppGroup = "group.eu.twonly.runtime"
+private struct NativeNotificationResponse: Decodable {
+ let ok: Bool
+ let batch: NativeNotificationBatch?
+ let error: String?
}
-func getPushNotificationData(pushData: String) -> (
- title: String, body: String, notificationId: Int64
-)? {
+private struct NativeNotificationBatch: Decodable {
+ let additions: [NativeNotificationAddition]
+ let removals: [String]
+ let badgeCount: Int64
+ let completed: Bool
- guard let data = Data(base64Encoded: pushData) else {
- NSLog("Failed to decode base64 string")
- return nil
- }
-
- do {
- let pushData = try EncryptedPushNotification(serializedBytes: data)
-
- var pushNotification: PushNotification?
- var pushUser: PushUser?
-
- // Check the keyId
- if pushData.keyID == 0 {
- let key = "InsecureOnlyUsedForAddingContact".data(using: .utf8)!
- pushNotification = tryDecryptMessage(key: key, pushData: pushData)
- } else {
- let pushUsers = getPushUsers()
- if pushUsers != nil {
- for tryPushUser in pushUsers! {
- for pushKey in tryPushUser.pushKeys {
- if pushKey.id == pushData.keyID {
- pushNotification = tryDecryptMessage(
- key: pushKey.key, pushData: pushData)
- if pushNotification != nil {
- pushUser = tryPushUser
- if isUUIDNewer(pushUser!.lastMessageID, pushNotification!.messageID)
- {
- //return ("blocked", "blocked", 0)
- }
- break
- }
- }
- }
- if pushUser != nil { break }
- }
- } else {
- NSLog("pushKeys are empty")
- }
- }
-
- if pushUser?.blocked == true {
- return ("blocked", "blocked", 0)
- }
-
- // Handle the push notification based on the pushKind
- if let pushNotification = pushNotification {
-
- if pushNotification.kind == .testNotification {
- return ("Test Notification", "This is a test notification.", 0)
- } else if pushUser != nil {
- return (
- pushUser!.displayName,
- getPushNotificationText(pushNotification: pushNotification, userKnown: true).0, pushUser!.userID
- )
- } else {
- let content = getPushNotificationText(pushNotification: pushNotification, userKnown: false)
- return (
- content.1, content.0, 1
- )
- }
-
- } else {
- NSLog("Failed to decrypt message or pushKind is nil")
- }
- return nil
- } catch {
- NSLog("Error decoding JSON: \(error)")
- return nil
- }
+ enum CodingKeys: String, CodingKey {
+ case additions, removals, completed
+ case badgeCount = "badge_count"
+ }
}
-func isUUIDNewer(_ uuid1: String, _ uuid2: String) -> Bool {
- guard uuid1.count >= 8, uuid2.count >= 8 else { return true }
- let hex1 = String(uuid1.prefix(8))
- let hex2 = String(uuid2.prefix(8))
- guard let timestamp1 = UInt32(hex1, radix: 16),
- let timestamp2 = UInt32(hex2, radix: 16)
- else { return true }
- return timestamp1 > timestamp2
+private struct NativeNotificationAddition: Decodable {
+ let eventId: String
+ let notificationId: String
+ let conversationId: String?
+ let senderId: Int64
+ let senderName: String
+ let title: String
+ let body: String
+ let conversationName: String?
+ let isGroup: Bool
+ let messageId: String?
+ let kind: String
+ let content: String?
+ let createdAt: Int64
+ let avatarPath: String?
+
+ enum CodingKeys: String, CodingKey {
+ case kind, content, title, body
+ case eventId = "event_id"
+ case notificationId = "notification_id"
+ case conversationId = "conversation_id"
+ case senderId = "sender_id"
+ case senderName = "sender_name"
+ case conversationName = "conversation_name"
+ case isGroup = "is_group"
+ case messageId = "message_id"
+ case createdAt = "created_at"
+ case avatarPath = "avatar_path"
+ }
}
-func tryDecryptMessage(key: Data, pushData: EncryptedPushNotification) -> PushNotification? {
+final class NotificationService: UNNotificationServiceExtension {
+ private let finishLock = NSLock()
+ private var hasFinished = false
+ private var contentHandler: ((UNNotificationContent) -> Void)?
- do {
- // Create a nonce for ChaChaPoly
- let nonce = try ChaChaPoly.Nonce(data: pushData.nonce)
+ override func didReceive(
+ _ request: UNNotificationRequest,
+ withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
+ ) {
+ self.contentHandler = contentHandler
- // Create a sealed box for ChaChaPoly
- let sealedBox = try ChaChaPoly.SealedBox(
- nonce: nonce,
- ciphertext: pushData.ciphertext,
- tag: pushData.mac
- )
-
- // Decrypt the data using the key
- let decryptedData = try ChaChaPoly.open(sealedBox, using: SymmetricKey(data: key))
-
- // Here you can determine the PushKind based on the decrypted message
- return try PushNotification(serializedBytes: decryptedData)
- } catch {
- NSLog("Decryption failed: \(error)")
+ guard let runtimeDirectory = Self.runtimeDirectory() else {
+ suppress(reason: "shared runtime directory is unavailable")
+ return
}
- return nil
-}
-
-func getPushUsers() -> [PushUser]? {
- // Retrieve the data from secure storage (Keychain)
- guard let pushUsersB64 = readFromKeychain(key: "push_keys_receiving") else {
- NSLog("No data found for key: push_keys_receiving")
- return nil
- }
- guard let pushUsersBytes = Data(base64Encoded: pushUsersB64) else {
- NSLog("Failed to decode base64 push users")
- return nil
- }
-
- do {
- let pushUsers = try PushUsers(serializedBytes: pushUsersBytes)
- return pushUsers.users
- } catch {
- NSLog("Error decoding JSON: \(error)")
- return nil
- }
-}
-
-// Helper function to read from Keychain
-func readFromKeychain(key: String) -> String? {
- let query: [String: Any] = [
- kSecClass as String: kSecClassGenericPassword,
- kSecAttrAccount as String: key,
- kSecAttrService as String: "flutter_secure_storage_service",
- kSecReturnData as String: kCFBooleanTrue!,
- kSecMatchLimit as String: kSecMatchLimitOne,
- kSecAttrAccessGroup as String: "CN332ZUGRP.eu.twonly.shared", // Use your access group
- ]
-
- var dataTypeRef: AnyObject? = nil
- let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)
-
- if status == errSecSuccess {
- if let data = dataTypeRef as? Data {
- return String(data: data, encoding: .utf8)
- }
- }
-
- return nil
-}
-
-// Helper function to write to Keychain
-func writeToKeychain(key: String, value: String) {
- guard let data = value.data(using: .utf8) else {
- NSLog("Failed to convert value to data for keychain key: \(key)")
+ DispatchQueue.global(qos: .userInitiated).async {
+ guard let response = Self.processWakeup(runtimeDirectory: runtimeDirectory) else {
+ self.suppress(reason: "notification worker returned no response")
return
+ }
+ guard response.ok,
+ let batch = response.batch,
+ !batch.additions.isEmpty
+ else {
+ self.suppress(reason: response.error ?? "notification worker returned no messages")
+ return
+ }
+ self.render(batch: batch, original: request.content)
}
+ }
- let query: [String: Any] = [
- kSecClass as String: kSecClassGenericPassword,
- kSecAttrAccount as String: key,
- kSecAttrService as String: "flutter_secure_storage_service",
- kSecAttrAccessGroup as String: "CN332ZUGRP.eu.twonly.shared"
- ]
+ override func serviceExtensionTimeWillExpire() {
+ suppress(reason: "notification service extension timed out")
+ }
- // Delete existing item first to ensure a clean overwrite
- SecItemDelete(query as CFDictionary)
+ private func render(batch: NativeNotificationBatch, original: UNNotificationContent) {
+ let center = UNUserNotificationCenter.current()
+ let group = DispatchGroup()
+ let stateLock = NSLock()
+ var deliveredEventIds: [String] = []
+ var finalContent: UNNotificationContent = original
- // Add the new item with background-compatible accessibility
- var addQuery = query
- addQuery[kSecValueData as String] = data
- addQuery[kSecAttrAccessible as String] = kSecAttrAccessibleAfterFirstUnlock
-
- let status = SecItemAdd(addQuery as CFDictionary, nil)
- if status != errSecSuccess {
- NSLog("Failed to write keychain item for key \(key): \(status)")
- } else {
- NSLog("Successfully wrote keychain item for key: \(key)")
- }
-}
-
-func getPushNotificationText(pushNotification: PushNotification, userKnown: Bool) -> (String, String) {
- let systemLanguage = Locale.current.language.languageCode?.identifier ?? "en" // Get the current system language
-
- var pushNotificationText: [PushKind: String] = [:]
- var title = "You"
- var noTranslationFoundTitle = "You have a new message."
- var noTranslationFoundBody = "Open twonly to learn more."
-
- // Define the messages based on the system language
- if systemLanguage.contains("de") { // German
- title = "Du"
- noTranslationFoundTitle = "Du hast eine neue Nachricht."
- noTranslationFoundBody = "Öffne twonly um mehr zu erfahren."
- if (userKnown) {
- pushNotificationText = [
- .text: "hat eine Nachricht{inGroup} gesendet.",
- .twonly: "hat ein twonly{inGroup} gesendet.",
- .video: "hat ein Video{inGroup} gesendet.",
- .image: "hat ein Bild{inGroup} gesendet.",
- .audio: "hat eine Sprachnachricht{inGroup} gesendet.",
- .contactRequest: "möchte sich mit dir vernetzen.",
- .acceptRequest: "ist jetzt mit dir vernetzt.",
- .storedMediaFile: "hat dein Bild gespeichert.",
- .reaction: "hat auf dein Bild reagiert.",
- .testNotification: "Das ist eine Testbenachrichtigung.",
- .reopenedMedia: "hat dein Bild erneut geöffnet.",
- .reactionToVideo: "hat mit {{content}} auf dein Video reagiert.",
- .reactionToText: "hat mit {{content}} auf deinen Text reagiert.",
- .reactionToImage: "hat mit {{content}} auf dein Bild reagiert.",
- .reactionToAudio: "hat mit {{content}} auf deine Sprachnachricht reagiert.",
- .response: "hat dir{inGroup} geantwortet.",
- .addedToGroup: "hat dich zu \"{{content}}\" hinzugefügt.",
- ]
- } else {
- pushNotificationText = [
- .contactRequest: "hast eine neue Kontaktanfrage erhalten.",
- ]
+ for (index, addition) in batch.additions.enumerated() {
+ group.enter()
+ communicationContent(for: addition, badgeCount: batch.badgeCount, original: original) {
+ content in
+ let isFinal = index == batch.additions.count - 1
+ if isFinal {
+ stateLock.lock()
+ finalContent = content
+ deliveredEventIds.append(addition.eventId)
+ stateLock.unlock()
+ group.leave()
+ return
}
- } else {
- if (userKnown) {
- pushNotificationText = [
- .text: "sent a message{inGroup}.",
- .twonly: "sent a twonly{inGroup}.",
- .video: "sent a video{inGroup}.",
- .image: "sent an image{inGroup}.",
- .audio: "sent a voice message{inGroup}.",
- .contactRequest: "wants to connect with you.",
- .acceptRequest: "is now connected with you.",
- .storedMediaFile: "has stored your image.",
- .reaction: "has reacted to your image.",
- .testNotification: "This is a test notification.",
- .reopenedMedia: "has reopened your image.",
- .reactionToVideo: "has reacted with {{content}} to your video.",
- .reactionToText: "has reacted with {{content}} to your text.",
- .reactionToImage: "has reacted with {{content}} to your image.",
- .reactionToAudio: "has reacted with {{content}} to your voice message.",
- .response: "has responded{inGroup}.",
- .addedToGroup: "has added you to \"{{content}}\"",
- ]
- } else {
- pushNotificationText = [
- .contactRequest: "have received a new contact request.",
- ]
+
+ let request = UNNotificationRequest(
+ identifier: addition.notificationId,
+ content: content,
+ trigger: nil
+ )
+ center.add(request) { error in
+ if let error {
+ NSLog("Could not schedule Twonly notification: \(error)")
+ } else {
+ stateLock.lock()
+ deliveredEventIds.append(addition.eventId)
+ stateLock.unlock()
+ }
+ group.leave()
}
+ }
}
- var content = pushNotificationText[pushNotification.kind] ?? ""
- if (content == "") {
- title = noTranslationFoundTitle
- content = noTranslationFoundBody
+ group.notify(queue: .global(qos: .userInitiated)) { [weak self] in
+ stateLock.lock()
+ let eventIds = deliveredEventIds
+ let content = finalContent
+ stateLock.unlock()
+ Self.acknowledge(eventIds: eventIds)
+ self?.finish(with: content)
}
+ }
- if pushNotification.hasAdditionalContent {
- content.replace("{{content}}", with: pushNotification.additionalContent)
- content.replace("{inGroup}", with: " in {inGroup}")
- content.replace("{inGroup}", with: pushNotification.additionalContent)
- } else {
- content.replace("{inGroup}", with: "")
+ private func communicationContent(
+ for addition: NativeNotificationAddition,
+ badgeCount: Int64,
+ original: UNNotificationContent,
+ completion: @escaping (UNNotificationContent) -> Void
+ ) {
+ let mutable = (original.mutableCopy() as? UNMutableNotificationContent)
+ ?? UNMutableNotificationContent()
+ mutable.title = addition.title
+ mutable.body = addition.body
+ mutable.threadIdentifier = addition.conversationId ?? String(addition.senderId)
+ mutable.badge = NSNumber(value: badgeCount)
+ mutable.sound = .default
+ var userInfo = mutable.userInfo
+ if let conversationId = addition.conversationId {
+ userInfo["conversation_id"] = conversationId
}
+ userInfo["notification_id"] = addition.notificationId
+ mutable.userInfo = userInfo
+
+ let avatar = addition.avatarPath
+ .flatMap { try? Data(contentsOf: URL(fileURLWithPath: $0)) }
+ .map(INImage.init(imageData:))
+ let sender = INPerson(
+ personHandle: INPersonHandle(value: String(addition.senderId), type: .unknown),
+ nameComponents: nil,
+ displayName: addition.senderName,
+ image: avatar,
+ contactIdentifier: nil,
+ customIdentifier: String(addition.senderId)
+ )
+ let groupName = addition.isGroup
+ ? addition.conversationName.map(INSpeakableString.init(spokenPhrase:))
+ : nil
+ let intent = INSendMessageIntent(
+ recipients: nil,
+ outgoingMessageType: .outgoingMessageText,
+ content: addition.body,
+ speakableGroupName: groupName,
+ conversationIdentifier: addition.conversationId ?? String(addition.senderId),
+ serviceName: "Twonly",
+ sender: sender,
+ attachments: nil
+ )
+ let interaction = INInteraction(intent: intent, response: nil)
+ interaction.direction = .incoming
+ interaction.donate { error in
+ if let error {
+ NSLog("Could not donate Twonly communication intent: \(error)")
+ completion(mutable)
+ return
+ }
+ do {
+ completion(try mutable.updating(from: intent))
+ } catch {
+ NSLog("Could not create Twonly communication notification: \(error)")
+ completion(mutable)
+ }
+ }
+ }
+
+ private func finish(with content: UNNotificationContent) {
+ finishLock.lock()
+ guard !hasFinished else {
+ finishLock.unlock()
+ return
+ }
+ hasFinished = true
+ let handler = contentHandler
+ contentHandler = nil
+ finishLock.unlock()
+ handler?(content)
+ }
+
+ private func suppress(reason: String) {
+ NSLog("Suppressing Twonly wake-up notification: \(reason)")
+ finish(with: UNNotificationContent())
+ }
+
+ private static func runtimeDirectory() -> String? {
+ guard
+ let container = FileManager.default.containerURL(
+ forSecurityApplicationGroupIdentifier: runtimeAppGroup
+ )
+ else { return nil }
+ let directory = container.appendingPathComponent("runtime", isDirectory: true)
+ do {
+ try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
+ try (directory as NSURL).setResourceValue(
+ URLFileProtection.completeUntilFirstUserAuthentication,
+ forKey: .fileProtectionKey
+ )
+ return directory.path
+ } catch {
+ NSLog("Could not open Twonly runtime directory: \(error)")
+ return nil
+ }
+ }
+
+ private static func processWakeup(runtimeDirectory: String) -> NativeNotificationResponse? {
+ let locale = Locale.current.identifier
+ let pointer = runtimeDirectory.withCString { databaseDirectory in
+ runtimeDirectory.withCString { dataDirectory in
+ locale.withCString { locale in
+ twonly_notification_process(databaseDirectory, dataDirectory, locale, 24_000)
+ }
+ }
+ }
+ guard let pointer else { return nil }
+ defer { twonly_notification_string_free(pointer) }
+ let json = String(cString: pointer)
+ do {
+ return try JSONDecoder().decode(
+ NativeNotificationResponse.self,
+ from: Data(json.utf8)
+ )
+ } catch {
+ NSLog("Could not decode Twonly notification worker response: \(error)")
+ return nil
+ }
+ }
+
+ private static func acknowledge(eventIds: [String]) {
+ guard !eventIds.isEmpty, let data = try? JSONEncoder().encode(eventIds),
+ let json = String(data: data, encoding: .utf8)
+ else { return }
+ let pointer = json.withCString { twonly_notification_acknowledge($0) }
+ guard let pointer else { return }
+ twonly_notification_string_free(pointer)
+ }
- // Return the corresponding message or an empty string if not found
- return (content, title)
}
diff --git a/ios/NotificationService/push_notification.pb.swift b/ios/NotificationService/push_notification.pb.swift
deleted file mode 100644
index 09b28f19..00000000
--- a/ios/NotificationService/push_notification.pb.swift
+++ /dev/null
@@ -1,447 +0,0 @@
-// DO NOT EDIT.
-// swift-format-ignore-file
-// swiftlint:disable all
-//
-// Generated by the Swift generator plugin for the protocol buffer compiler.
-// Source: push_notification.proto
-//
-// For information on using the generated types, please see the documentation:
-// https://github.com/apple/swift-protobuf/
-
-#if canImport(FoundationEssentials)
-import FoundationEssentials
-#else
-import Foundation
-#endif
-import SwiftProtobuf
-
-// If the compiler emits an error on this type, it is because this file
-// was generated by a version of the `protoc` Swift plug-in that is
-// incompatible with the version of SwiftProtobuf to which you are linking.
-// Please ensure that you are building against the same version of the API
-// that was used to generate this file.
-fileprivate nonisolated struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
- struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
- typealias Version = _2
-}
-
-nonisolated enum PushKind: SwiftProtobuf.Enum, Swift.CaseIterable {
- typealias RawValue = Int
- case reaction // = 0
- case response // = 1
- case text // = 2
- case video // = 3
- case twonly // = 4
- case image // = 5
- case contactRequest // = 6
- case acceptRequest // = 7
- case storedMediaFile // = 8
- case testNotification // = 9
- case reopenedMedia // = 10
- case reactionToVideo // = 11
- case reactionToText // = 12
- case reactionToImage // = 13
- case reactionToAudio // = 14
- case addedToGroup // = 15
- case audio // = 16
- case UNRECOGNIZED(Int)
-
- init() {
- self = .reaction
- }
-
- init?(rawValue: Int) {
- switch rawValue {
- case 0: self = .reaction
- case 1: self = .response
- case 2: self = .text
- case 3: self = .video
- case 4: self = .twonly
- case 5: self = .image
- case 6: self = .contactRequest
- case 7: self = .acceptRequest
- case 8: self = .storedMediaFile
- case 9: self = .testNotification
- case 10: self = .reopenedMedia
- case 11: self = .reactionToVideo
- case 12: self = .reactionToText
- case 13: self = .reactionToImage
- case 14: self = .reactionToAudio
- case 15: self = .addedToGroup
- case 16: self = .audio
- default: self = .UNRECOGNIZED(rawValue)
- }
- }
-
- var rawValue: Int {
- switch self {
- case .reaction: return 0
- case .response: return 1
- case .text: return 2
- case .video: return 3
- case .twonly: return 4
- case .image: return 5
- case .contactRequest: return 6
- case .acceptRequest: return 7
- case .storedMediaFile: return 8
- case .testNotification: return 9
- case .reopenedMedia: return 10
- case .reactionToVideo: return 11
- case .reactionToText: return 12
- case .reactionToImage: return 13
- case .reactionToAudio: return 14
- case .addedToGroup: return 15
- case .audio: return 16
- case .UNRECOGNIZED(let i): return i
- }
- }
-
- // The compiler won't synthesize support with the UNRECOGNIZED case.
- static let allCases: [PushKind] = [
- .reaction,
- .response,
- .text,
- .video,
- .twonly,
- .image,
- .contactRequest,
- .acceptRequest,
- .storedMediaFile,
- .testNotification,
- .reopenedMedia,
- .reactionToVideo,
- .reactionToText,
- .reactionToImage,
- .reactionToAudio,
- .addedToGroup,
- .audio,
- ]
-
-}
-
-nonisolated struct EncryptedPushNotification: Sendable {
- // SwiftProtobuf.Message conformance is added in an extension below. See the
- // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
- // methods supported on all messages.
-
- var keyID: Int64 = 0
-
- var nonce: Data = Data()
-
- var ciphertext: Data = Data()
-
- var mac: Data = Data()
-
- var unknownFields = SwiftProtobuf.UnknownStorage()
-
- init() {}
-}
-
-nonisolated struct PushNotification: Sendable {
- // SwiftProtobuf.Message conformance is added in an extension below. See the
- // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
- // methods supported on all messages.
-
- var kind: PushKind = .reaction
-
- var messageID: String {
- get {_messageID ?? String()}
- set {_messageID = newValue}
- }
- /// Returns true if `messageID` has been explicitly set.
- var hasMessageID: Bool {self._messageID != nil}
- /// Clears the value of `messageID`. Subsequent reads from it will return its default value.
- mutating func clearMessageID() {self._messageID = nil}
-
- var additionalContent: String {
- get {_additionalContent ?? String()}
- set {_additionalContent = newValue}
- }
- /// Returns true if `additionalContent` has been explicitly set.
- var hasAdditionalContent: Bool {self._additionalContent != nil}
- /// Clears the value of `additionalContent`. Subsequent reads from it will return its default value.
- mutating func clearAdditionalContent() {self._additionalContent = nil}
-
- var unknownFields = SwiftProtobuf.UnknownStorage()
-
- init() {}
-
- fileprivate var _messageID: String? = nil
- fileprivate var _additionalContent: String? = nil
-}
-
-nonisolated struct PushUsers: Sendable {
- // SwiftProtobuf.Message conformance is added in an extension below. See the
- // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
- // methods supported on all messages.
-
- var users: [PushUser] = []
-
- var unknownFields = SwiftProtobuf.UnknownStorage()
-
- init() {}
-}
-
-nonisolated struct PushUser: Sendable {
- // SwiftProtobuf.Message conformance is added in an extension below. See the
- // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
- // methods supported on all messages.
-
- var userID: Int64 = 0
-
- var displayName: String = String()
-
- var blocked: Bool = false
-
- var lastMessageID: String {
- get {_lastMessageID ?? String()}
- set {_lastMessageID = newValue}
- }
- /// Returns true if `lastMessageID` has been explicitly set.
- var hasLastMessageID: Bool {self._lastMessageID != nil}
- /// Clears the value of `lastMessageID`. Subsequent reads from it will return its default value.
- mutating func clearLastMessageID() {self._lastMessageID = nil}
-
- var pushKeys: [PushKey] = []
-
- var unknownFields = SwiftProtobuf.UnknownStorage()
-
- init() {}
-
- fileprivate var _lastMessageID: String? = nil
-}
-
-nonisolated struct PushKey: Sendable {
- // SwiftProtobuf.Message conformance is added in an extension below. See the
- // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
- // methods supported on all messages.
-
- var id: Int64 = 0
-
- var key: Data = Data()
-
- var createdAtUnixTimestamp: Int64 = 0
-
- var unknownFields = SwiftProtobuf.UnknownStorage()
-
- init() {}
-}
-
-// MARK: - Code below here is support for the SwiftProtobuf runtime.
-
-nonisolated extension PushKind: SwiftProtobuf._ProtoNameProviding {
- static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0REACTION\0\u{1}RESPONSE\0\u{1}TEXT\0\u{1}VIDEO\0\u{1}TWONLY\0\u{1}IMAGE\0\u{1}CONTACT_REQUEST\0\u{1}ACCEPT_REQUEST\0\u{1}STORED_MEDIA_FILE\0\u{1}TEST_NOTIFICATION\0\u{1}REOPENED_MEDIA\0\u{1}REACTION_TO_VIDEO\0\u{1}REACTION_TO_TEXT\0\u{1}REACTION_TO_IMAGE\0\u{1}REACTION_TO_AUDIO\0\u{1}ADDED_TO_GROUP\0\u{1}AUDIO\0")
-}
-
-nonisolated extension EncryptedPushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
- static let protoMessageName: String = "EncryptedPushNotification"
- static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}key_id\0\u{1}nonce\0\u{1}ciphertext\0\u{1}mac\0")
-
- mutating func decodeMessage(decoder: inout D) throws {
- while let fieldNumber = try decoder.nextFieldNumber() {
- // The use of inline closures is to circumvent an issue where the compiler
- // allocates stack space for every case branch when no optimizations are
- // enabled. https://github.com/apple/swift-protobuf/issues/1034
- switch fieldNumber {
- case 1: try { try decoder.decodeSingularInt64Field(value: &self.keyID) }()
- case 2: try { try decoder.decodeSingularBytesField(value: &self.nonce) }()
- case 3: try { try decoder.decodeSingularBytesField(value: &self.ciphertext) }()
- case 4: try { try decoder.decodeSingularBytesField(value: &self.mac) }()
- default: break
- }
- }
- }
-
- func traverse(visitor: inout V) throws {
- if self.keyID != 0 {
- try visitor.visitSingularInt64Field(value: self.keyID, fieldNumber: 1)
- }
- if !self.nonce.isEmpty {
- try visitor.visitSingularBytesField(value: self.nonce, fieldNumber: 2)
- }
- if !self.ciphertext.isEmpty {
- try visitor.visitSingularBytesField(value: self.ciphertext, fieldNumber: 3)
- }
- if !self.mac.isEmpty {
- try visitor.visitSingularBytesField(value: self.mac, fieldNumber: 4)
- }
- try unknownFields.traverse(visitor: &visitor)
- }
-
- static func ==(lhs: EncryptedPushNotification, rhs: EncryptedPushNotification) -> Bool {
- if lhs.keyID != rhs.keyID {return false}
- if lhs.nonce != rhs.nonce {return false}
- if lhs.ciphertext != rhs.ciphertext {return false}
- if lhs.mac != rhs.mac {return false}
- if lhs.unknownFields != rhs.unknownFields {return false}
- return true
- }
-}
-
-nonisolated extension PushNotification: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
- static let protoMessageName: String = "PushNotification"
- static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}kind\0\u{3}message_id\0\u{3}additional_content\0")
-
- mutating func decodeMessage(decoder: inout D) throws {
- while let fieldNumber = try decoder.nextFieldNumber() {
- // The use of inline closures is to circumvent an issue where the compiler
- // allocates stack space for every case branch when no optimizations are
- // enabled. https://github.com/apple/swift-protobuf/issues/1034
- switch fieldNumber {
- case 1: try { try decoder.decodeSingularEnumField(value: &self.kind) }()
- case 2: try { try decoder.decodeSingularStringField(value: &self._messageID) }()
- case 3: try { try decoder.decodeSingularStringField(value: &self._additionalContent) }()
- default: break
- }
- }
- }
-
- func traverse(visitor: inout V) throws {
- // The use of inline closures is to circumvent an issue where the compiler
- // allocates stack space for every if/case branch local when no optimizations
- // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
- // https://github.com/apple/swift-protobuf/issues/1182
- if self.kind != .reaction {
- try visitor.visitSingularEnumField(value: self.kind, fieldNumber: 1)
- }
- try { if let v = self._messageID {
- try visitor.visitSingularStringField(value: v, fieldNumber: 2)
- } }()
- try { if let v = self._additionalContent {
- try visitor.visitSingularStringField(value: v, fieldNumber: 3)
- } }()
- try unknownFields.traverse(visitor: &visitor)
- }
-
- static func ==(lhs: PushNotification, rhs: PushNotification) -> Bool {
- if lhs.kind != rhs.kind {return false}
- if lhs._messageID != rhs._messageID {return false}
- if lhs._additionalContent != rhs._additionalContent {return false}
- if lhs.unknownFields != rhs.unknownFields {return false}
- return true
- }
-}
-
-nonisolated extension PushUsers: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
- static let protoMessageName: String = "PushUsers"
- static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}users\0")
-
- mutating func decodeMessage(decoder: inout D) throws {
- while let fieldNumber = try decoder.nextFieldNumber() {
- // The use of inline closures is to circumvent an issue where the compiler
- // allocates stack space for every case branch when no optimizations are
- // enabled. https://github.com/apple/swift-protobuf/issues/1034
- switch fieldNumber {
- case 1: try { try decoder.decodeRepeatedMessageField(value: &self.users) }()
- default: break
- }
- }
- }
-
- func traverse(visitor: inout V) throws {
- if !self.users.isEmpty {
- try visitor.visitRepeatedMessageField(value: self.users, fieldNumber: 1)
- }
- try unknownFields.traverse(visitor: &visitor)
- }
-
- static func ==(lhs: PushUsers, rhs: PushUsers) -> Bool {
- if lhs.users != rhs.users {return false}
- if lhs.unknownFields != rhs.unknownFields {return false}
- return true
- }
-}
-
-nonisolated extension PushUser: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
- static let protoMessageName: String = "PushUser"
- static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}user_id\0\u{3}display_name\0\u{1}blocked\0\u{3}last_message_id\0\u{3}push_keys\0")
-
- mutating func decodeMessage(decoder: inout D) throws {
- while let fieldNumber = try decoder.nextFieldNumber() {
- // The use of inline closures is to circumvent an issue where the compiler
- // allocates stack space for every case branch when no optimizations are
- // enabled. https://github.com/apple/swift-protobuf/issues/1034
- switch fieldNumber {
- case 1: try { try decoder.decodeSingularInt64Field(value: &self.userID) }()
- case 2: try { try decoder.decodeSingularStringField(value: &self.displayName) }()
- case 3: try { try decoder.decodeSingularBoolField(value: &self.blocked) }()
- case 4: try { try decoder.decodeSingularStringField(value: &self._lastMessageID) }()
- case 5: try { try decoder.decodeRepeatedMessageField(value: &self.pushKeys) }()
- default: break
- }
- }
- }
-
- func traverse(visitor: inout V) throws {
- // The use of inline closures is to circumvent an issue where the compiler
- // allocates stack space for every if/case branch local when no optimizations
- // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
- // https://github.com/apple/swift-protobuf/issues/1182
- if self.userID != 0 {
- try visitor.visitSingularInt64Field(value: self.userID, fieldNumber: 1)
- }
- if !self.displayName.isEmpty {
- try visitor.visitSingularStringField(value: self.displayName, fieldNumber: 2)
- }
- if self.blocked != false {
- try visitor.visitSingularBoolField(value: self.blocked, fieldNumber: 3)
- }
- try { if let v = self._lastMessageID {
- try visitor.visitSingularStringField(value: v, fieldNumber: 4)
- } }()
- if !self.pushKeys.isEmpty {
- try visitor.visitRepeatedMessageField(value: self.pushKeys, fieldNumber: 5)
- }
- try unknownFields.traverse(visitor: &visitor)
- }
-
- static func ==(lhs: PushUser, rhs: PushUser) -> Bool {
- if lhs.userID != rhs.userID {return false}
- if lhs.displayName != rhs.displayName {return false}
- if lhs.blocked != rhs.blocked {return false}
- if lhs._lastMessageID != rhs._lastMessageID {return false}
- if lhs.pushKeys != rhs.pushKeys {return false}
- if lhs.unknownFields != rhs.unknownFields {return false}
- return true
- }
-}
-
-nonisolated extension PushKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
- static let protoMessageName: String = "PushKey"
- static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}id\0\u{1}key\0\u{3}created_at_unix_timestamp\0")
-
- mutating func decodeMessage(decoder: inout D) throws {
- while let fieldNumber = try decoder.nextFieldNumber() {
- // The use of inline closures is to circumvent an issue where the compiler
- // allocates stack space for every case branch when no optimizations are
- // enabled. https://github.com/apple/swift-protobuf/issues/1034
- switch fieldNumber {
- case 1: try { try decoder.decodeSingularInt64Field(value: &self.id) }()
- case 2: try { try decoder.decodeSingularBytesField(value: &self.key) }()
- case 3: try { try decoder.decodeSingularInt64Field(value: &self.createdAtUnixTimestamp) }()
- default: break
- }
- }
- }
-
- func traverse(visitor: inout V) throws {
- if self.id != 0 {
- try visitor.visitSingularInt64Field(value: self.id, fieldNumber: 1)
- }
- if !self.key.isEmpty {
- try visitor.visitSingularBytesField(value: self.key, fieldNumber: 2)
- }
- if self.createdAtUnixTimestamp != 0 {
- try visitor.visitSingularInt64Field(value: self.createdAtUnixTimestamp, fieldNumber: 3)
- }
- try unknownFields.traverse(visitor: &visitor)
- }
-
- static func ==(lhs: PushKey, rhs: PushKey) -> Bool {
- if lhs.id != rhs.id {return false}
- if lhs.key != rhs.key {return false}
- if lhs.createdAtUnixTimestamp != rhs.createdAtUnixTimestamp {return false}
- if lhs.unknownFields != rhs.unknownFields {return false}
- return true
- }
-}
diff --git a/ios/Podfile b/ios/Podfile
index 0ed93e4f..34ad70b0 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -97,6 +97,5 @@ post_install do |installer|
end
target 'NotificationService' do
- pod 'SwiftProtobuf'
- # pod 'Firebase/Messaging'
+ pod 'rust_lib_twonly', :path => '.symlinks/plugins/rust_lib_twonly/ios'
end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 04f4a14f..b9c4642b 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -93,8 +93,7 @@ PODS:
- permission_handler_apple (9.3.0):
- Flutter
- PromisesObjC (2.4.0)
- - rust_lib_twonly (0.0.1):
- - Flutter
+ - rust_lib_twonly (0.0.1)
- screen_protector (1.5.1):
- Flutter
- ScreenProtectorKit (= 1.5.1)
@@ -199,7 +198,7 @@ SPEC CHECKSUMS:
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
- rust_lib_twonly: 73165b05d0cda50db45852db63f49caa7f319520
+ rust_lib_twonly: 6586fdf02e31cd8a3ad9f1ee84796da6bbf289bb
screen_protector: 18c6aca2dc5d2a832f6787a5318f97f03e9d3150
ScreenProtectorKit: 6ceb3e0808341a9bc15d175bff40dfdd4b32da71
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
@@ -207,6 +206,6 @@ SPEC CHECKSUMS:
SwiftProtobuf: d724b5145bfc609d9a49c1e3e3a3dabb07273ffb
workmanager_apple: 904529ae31e97fc5be632cf628507652294a0778
-PODFILE CHECKSUM: 245e6d5f26c858edb6b99a7d972cc93ead4d55cf
+PODFILE CHECKSUM: f83bbaaed0b8c29b006472e50864d40e54617e24
COCOAPODS: 1.17.0
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 60389e6f..054601de 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 54;
+ objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
@@ -14,6 +14,7 @@
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@@ -23,7 +24,6 @@
D25D4D7A2EFF41DB0029F805 /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D25D4D702EFF41DB0029F805 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
D2B2E0FF2F63819600E729C1 /* VideoCompressionChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2B2E0FE2F63819600E729C1 /* VideoCompressionChannel.swift */; };
F3C66D726A2EB28484DF0B10 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 16FBC6F5B58E1C6646F5D447 /* GoogleService-Info.plist */; };
- 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -91,6 +91,7 @@
70E8A5E1DA4031C0E3F86C77 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
7FC59147CD9A45BFAC98EA05 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
@@ -115,7 +116,6 @@
E96A5ACA32A7118204F050A5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
EE2CCFEE4ABECF33852F7735 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F02F7A1D63544AA9F23A1085 /* Pods-NotificationService.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationService.profile.xcconfig"; path = "Target Support Files/Pods-NotificationService/Pods-NotificationService.profile.xcconfig"; sourceTree = ""; };
- 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
@@ -311,9 +311,6 @@
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
- packageProductDependencies = (
- 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
- );
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
@@ -335,6 +332,9 @@
D25D4D792EFF41DB0029F805 /* PBXTargetDependency */,
);
name = Runner;
+ packageProductDependencies = (
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
+ );
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
@@ -385,9 +385,6 @@
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
- packageReferences = (
- 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
- );
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
@@ -419,6 +416,9 @@
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
+ packageReferences = (
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
+ );
preferredProjectObjectVersion = 77;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
@@ -1319,12 +1319,14 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+
/* Begin XCLocalSwiftPackageReference section */
- 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */
+
/* Begin XCSwiftPackageProductDependency section */
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
diff --git a/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 3a5a88ff..e6556149 100644
--- a/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/app-check.git",
"state" : {
- "revision" : "61b85103a1aeed8218f17c794687781505fbbef5",
- "version" : "11.2.0"
+ "revision" : "3e33dd27dd4c69bd81c7c81fe61d8ccf58846902",
+ "version" : "11.3.1"
}
},
{
@@ -23,8 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk",
"state" : {
- "revision" : "8d5b4189f1f482df8d5c58c9985ea70491ef5382",
- "version" : "12.14.0"
+ "revision" : "346daa9f46316aa372b35b317e18224acc2e9063",
+ "version" : "12.18.0"
}
},
{
@@ -41,8 +41,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/googleads/google-ads-on-device-conversion-ios-sdk",
"state" : {
- "revision" : "9bfcc6cf435b2e7c5562c1900b8680c594fa9a64",
- "version" : "3.6.0"
+ "revision" : "dc39082d8881109d35b94b1c122164c0e8d08a55",
+ "version" : "3.6.1"
}
},
{
@@ -50,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
- "revision" : "219e564a8510e983e675c94f77f7f7c50049f22d",
- "version" : "12.14.0"
+ "revision" : "f04760d460296cc0fa430935a7be212e5bd67fc5",
+ "version" : "12.18.0"
}
},
{
@@ -59,8 +59,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
- "revision" : "617af071af9aa1d6a091d59a202910ac482128f9",
- "version" : "10.1.0"
+ "revision" : "ba3358d3c3dbae8ef230b58a46b97ad65e84e974",
+ "version" : "10.1.1"
}
},
{
@@ -68,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
- "revision" : "60da361632d0de02786f709bdc0c4df340f7613e",
- "version" : "8.1.0"
+ "revision" : "92c8f6dc3ac375d6febdfcb3db68bc3d10633db3",
+ "version" : "8.1.3"
}
},
{
@@ -86,8 +86,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
- "revision" : "c0ac7575d70050c2973ba2318bd5af47f8e8153a",
- "version" : "5.3.0"
+ "revision" : "724a52eea6329b7e12d3ad8300d76ca9f3895fcc",
+ "version" : "5.3.1"
}
},
{
@@ -113,8 +113,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
- "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1",
- "version" : "2.30910.0"
+ "revision" : "3851d94a41890dea16dc3db34caf60e585cb4163",
+ "version" : "2.30910.1"
}
},
{
@@ -122,8 +122,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
- "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac",
- "version" : "2.4.0"
+ "revision" : "f4a19a3c313dc2616c70bb49d29a799fb16be837",
+ "version" : "2.4.1"
}
},
{
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 88277029..d5bb7aef 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -22,6 +22,9 @@ import workmanager_apple
WorkmanagerPlugin.setPluginRegistrantCallback { registry in
GeneratedPluginRegistrant.register(with: registry)
+ // Background tasks call AppEnvironment.init() too, so this engine needs
+ // the runtime storage channel just as much as the implicit one.
+ RuntimeStorageChannel.register(with: registry)
}
WorkmanagerPlugin.registerPeriodicTask(
@@ -51,6 +54,8 @@ import workmanager_apple
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
+ RuntimeStorageChannel.register(with: engineBridge.pluginRegistry)
+ NativeNotificationChannel.register(with: engineBridge.pluginRegistry)
}
override func userNotificationCenter(
@@ -70,23 +75,139 @@ import workmanager_apple
) {
NSLog("userNotificationCenter:willPresent")
- /*
- debugging NotificationService
- let pushKeys = getPushKey();
- print(pushKeys)
-
- let bestAttemptContent = notification.request.content
-
- guard let _userInfo = bestAttemptContent.userInfo as? [String: Any],
- let push_data = bestAttemptContent.userInfo["push_data"] as? String else {
- return completionHandler([.alert, .sound])
- }
-
- let data = getPushNotificationData(pushDataJson: push_data)
- print(data)
- */
-
completionHandler([.alert, .sound])
}
-}
\ No newline at end of file
+}
+
+/// Withdraws only the native notifications whose message IDs Dart reports as
+/// opened. The notification service extension's final alert keeps APNs' request
+/// identifier, so both the identifier and our `notification_id` user-info field
+/// have to be considered.
+class NativeNotificationChannel {
+ private static let channelName = "eu.twonly/notificationTap"
+ private static let notificationIdsKey = "notification_ids"
+
+ static func register(with registry: FlutterPluginRegistry) {
+ guard let registrar = registry.registrar(forPlugin: "TwonlyNativeNotifications") else {
+ return
+ }
+ let channel = FlutterMethodChannel(
+ name: channelName,
+ binaryMessenger: registrar.messenger()
+ )
+ channel.setMethodCallHandler { call, result in
+ guard call.method == "cancelNotifications" else {
+ result(FlutterMethodNotImplemented)
+ return
+ }
+ guard
+ let arguments = call.arguments as? [String: Any],
+ let values = arguments[notificationIdsKey] as? [String]
+ else {
+ result(
+ FlutterError(
+ code: "invalid_notification_ids",
+ message: "notification_ids must be a list of strings",
+ details: nil
+ ))
+ return
+ }
+ removeNotifications(Set(values), completion: result)
+ }
+ }
+
+ private static func removeNotifications(
+ _ notificationIds: Set,
+ completion: @escaping FlutterResult
+ ) {
+ guard !notificationIds.isEmpty else {
+ completion(nil)
+ return
+ }
+ let center = UNUserNotificationCenter.current()
+ center.getDeliveredNotifications { delivered in
+ let requestIds = delivered.compactMap { notification -> String? in
+ let request = notification.request
+ let notificationId = request.content.userInfo["notification_id"] as? String
+ return notificationIds.contains(request.identifier)
+ || notificationId.map(notificationIds.contains) == true
+ ? request.identifier
+ : nil
+ }
+ center.removeDeliveredNotifications(withIdentifiers: requestIds)
+ center.getPendingNotificationRequests { pending in
+ let pendingIds = pending.compactMap { request -> String? in
+ let notificationId = request.content.userInfo["notification_id"] as? String
+ return notificationIds.contains(request.identifier)
+ || notificationId.map(notificationIds.contains) == true
+ ? request.identifier
+ : nil
+ }
+ center.removePendingNotificationRequests(withIdentifiers: pendingIds)
+ DispatchQueue.main.async {
+ completion(nil)
+ }
+ }
+ }
+ }
+}
+
+/// Hands Dart the App Group container shared between the app and its
+/// extensions. Must be registered on every Flutter engine that runs
+/// `AppEnvironment.init()`, not just the implicit one.
+class RuntimeStorageChannel {
+ private static let appGroupIdentifier = "group.eu.twonly.runtime"
+
+ static func register(with registry: FlutterPluginRegistry) {
+ guard let registrar = registry.registrar(forPlugin: "TwonlyRuntimeStorage") else {
+ return
+ }
+ register(with: registrar.messenger())
+ }
+
+ static func register(with messenger: FlutterBinaryMessenger) {
+ let channel = FlutterMethodChannel(
+ name: "eu.twonly/runtime_storage",
+ binaryMessenger: messenger
+ )
+ channel.setMethodCallHandler { call, result in
+ guard call.method == "runtimeSupportDirectory" else {
+ result(FlutterMethodNotImplemented)
+ return
+ }
+ guard
+ let container = FileManager.default.containerURL(
+ forSecurityApplicationGroupIdentifier: appGroupIdentifier
+ )
+ else {
+ result(
+ FlutterError(
+ code: "runtime_app_group_unavailable",
+ message: "Could not open \(appGroupIdentifier)",
+ details: nil
+ ))
+ return
+ }
+ let runtimeDirectory = container.appendingPathComponent("runtime", isDirectory: true)
+ do {
+ try FileManager.default.createDirectory(
+ at: runtimeDirectory,
+ withIntermediateDirectories: true
+ )
+ try (runtimeDirectory as NSURL).setResourceValue(
+ URLFileProtection.completeUntilFirstUserAuthentication,
+ forKey: .fileProtectionKey
+ )
+ result(runtimeDirectory.path)
+ } catch {
+ result(
+ FlutterError(
+ code: "runtime_directory_failed",
+ message: error.localizedDescription,
+ details: nil
+ ))
+ }
+ }
+ }
+}
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 96bed792..8e45f74f 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -47,6 +47,10 @@
LSRequiresIPhoneOS
+ NSUserActivityTypes
+
+ INSendMessageIntent
+
NSCameraUsageDescription
Use your camera to make photos or videos and share them encrypted with your friends.
NSFaceIDUsageDescription
diff --git a/ios/Runner/Runner.entitlements b/ios/Runner/Runner.entitlements
index dc9d4ccf..2c5fb61b 100644
--- a/ios/Runner/Runner.entitlements
+++ b/ios/Runner/Runner.entitlements
@@ -8,9 +8,12 @@
applinks:me.twonly.eu
+ com.apple.developer.usernotifications.communication
+
com.apple.security.application-groups
group.eu.twonly.shareIntent
+ group.eu.twonly.runtime
keychain-access-groups
diff --git a/ios/Runner/RunnerDebug.entitlements b/ios/Runner/RunnerDebug.entitlements
index dc9d4ccf..2c5fb61b 100644
--- a/ios/Runner/RunnerDebug.entitlements
+++ b/ios/Runner/RunnerDebug.entitlements
@@ -8,9 +8,12 @@
applinks:me.twonly.eu
+ com.apple.developer.usernotifications.communication
+
com.apple.security.application-groups
group.eu.twonly.shareIntent
+ group.eu.twonly.runtime
keychain-access-groups
diff --git a/ios/Runner/RunnerRelease.entitlements b/ios/Runner/RunnerRelease.entitlements
index dc9d4ccf..2c5fb61b 100644
--- a/ios/Runner/RunnerRelease.entitlements
+++ b/ios/Runner/RunnerRelease.entitlements
@@ -8,9 +8,12 @@
applinks:me.twonly.eu
+ com.apple.developer.usernotifications.communication
+
com.apple.security.application-groups
group.eu.twonly.shareIntent
+ group.eu.twonly.runtime
keychain-access-groups
diff --git a/lib/core/bridge.dart b/lib/core/bridge.dart
index acd01aeb..e479d417 100644
--- a/lib/core/bridge.dart
+++ b/lib/core/bridge.dart
@@ -8,6 +8,7 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
// These functions are ignored because they are not marked as `pub`: `get_twonly_flutter`
// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `AnnouncedUser`, `OtherPromotion`
+// These functions are ignored (category: IgnoreBecauseExplicitAttribute): `initialize_twonly_notification`
Future initializeTwonlyFlutter({required InitConfig config}) =>
RustLib.instance.api.crateBridgeInitializeTwonlyFlutter(config: config);
diff --git a/lib/core/bridge/wrapper/app_database.dart b/lib/core/bridge/wrapper/app_database.dart
index 9cd4341e..b218f54e 100644
--- a/lib/core/bridge/wrapper/app_database.dart
+++ b/lib/core/bridge/wrapper/app_database.dart
@@ -52,6 +52,19 @@ class LegacyTableMigrationCount {
class RustAppDatabase {
const RustAppDatabase();
+ /// Streams the tables Rust has committed to.
+ ///
+ /// Rust owns the connection, so writes it makes on its own never pass
+ /// through the Drift compatibility executor and cannot invalidate Drift's
+ /// query streams. Dart forwards each batch into `notifyUpdates` so
+ /// `watch()` keeps reflecting Rust-side writes.
+ ///
+ /// An empty list means "assume every table changed". It is sent right
+ /// after (re)subscribing, and whenever the broadcast channel drops
+ /// notifications, so Dart never silently keeps stale rows on screen.
+ static Stream> changes() => RustLib.instance.api
+ .crateBridgeWrapperAppDatabaseRustAppDatabaseChanges();
+
static Future execute({
required String statement,
required List arguments,
diff --git a/lib/core/frb_generated.dart b/lib/core/frb_generated.dart
index b4835415..c681f6ec 100644
--- a/lib/core/frb_generated.dart
+++ b/lib/core/frb_generated.dart
@@ -84,7 +84,7 @@ class RustLib extends BaseEntrypoint {
String get codegenVersion => '2.12.0';
@override
- int get rustContentHash => -1286774374;
+ int get rustContentHash => -1780439173;
static const kDefaultExternalLibraryLoaderConfig =
ExternalLibraryLoaderConfig(
@@ -409,6 +409,8 @@ abstract class RustLibApi extends BaseApi {
required List prekeys,
});
+ Stream> crateBridgeWrapperAppDatabaseRustAppDatabaseChanges();
+
Future
crateBridgeWrapperAppDatabaseRustAppDatabaseExecute({
required String statement,
@@ -3283,6 +3285,43 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
],
);
+ @override
+ Stream> crateBridgeWrapperAppDatabaseRustAppDatabaseChanges() {
+ final sink = RustStreamSink>();
+ unawaited(
+ handler.executeNormal(
+ NormalTask(
+ callFfi: (port_) {
+ final serializer = SseSerializer(generalizedFrbRustBinding);
+ sse_encode_StreamSink_list_String_Sse(sink, serializer);
+ pdeCallFfi(
+ generalizedFrbRustBinding,
+ serializer,
+ funcId: 77,
+ port: port_,
+ );
+ },
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_unit,
+ decodeErrorData: sse_decode_AnyhowException,
+ ),
+ constMeta:
+ kCrateBridgeWrapperAppDatabaseRustAppDatabaseChangesConstMeta,
+ argValues: [sink],
+ apiImpl: this,
+ ),
+ ),
+ );
+ return sink.stream;
+ }
+
+ TaskConstMeta
+ get kCrateBridgeWrapperAppDatabaseRustAppDatabaseChangesConstMeta =>
+ const TaskConstMeta(
+ debugName: "rust_app_database_changes",
+ argNames: ["sink"],
+ );
+
@override
Future
crateBridgeWrapperAppDatabaseRustAppDatabaseExecute({
@@ -3298,7 +3337,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 77,
+ funcId: 78,
port: port_,
);
},
@@ -3331,7 +3370,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 78,
+ funcId: 79,
port: port_,
);
},
@@ -3364,7 +3403,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 79,
+ funcId: 80,
port: port_,
);
},
@@ -3401,7 +3440,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 80,
+ funcId: 81,
port: port_,
);
},
@@ -3433,7 +3472,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 81,
+ funcId: 82,
port: port_,
);
},
@@ -3466,7 +3505,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 82,
+ funcId: 83,
port: port_,
);
},
@@ -3501,7 +3540,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 83,
+ funcId: 84,
port: port_,
);
},
@@ -3533,7 +3572,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 84,
+ funcId: 85,
port: port_,
);
},
@@ -3571,7 +3610,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 85,
+ funcId: 86,
port: port_,
);
},
@@ -3604,7 +3643,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 86,
+ funcId: 87,
port: port_,
);
},
@@ -3642,7 +3681,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 87,
+ funcId: 88,
port: port_,
);
},
@@ -3679,7 +3718,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 88,
+ funcId: 89,
port: port_,
);
},
@@ -3716,7 +3755,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 89,
+ funcId: 90,
port: port_,
);
},
@@ -3754,7 +3793,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 90,
+ funcId: 91,
port: port_,
);
},
@@ -3792,7 +3831,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 91,
+ funcId: 92,
port: port_,
);
},
@@ -3824,7 +3863,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 92,
+ funcId: 93,
port: port_,
);
},
@@ -3857,7 +3896,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 93,
+ funcId: 94,
port: port_,
);
},
@@ -3889,7 +3928,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 94,
+ funcId: 95,
port: port_,
);
},
@@ -3924,7 +3963,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 95,
+ funcId: 96,
port: port_,
);
},
@@ -3961,7 +4000,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 96,
+ funcId: 97,
port: port_,
);
},
@@ -3993,7 +4032,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 97,
+ funcId: 98,
port: port_,
);
},
@@ -4025,7 +4064,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 98,
+ funcId: 99,
port: port_,
);
},
@@ -4060,7 +4099,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 99,
+ funcId: 100,
port: port_,
);
},
@@ -4099,7 +4138,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 100,
+ funcId: 101,
port: port_,
);
},
@@ -4136,7 +4175,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 101,
+ funcId: 102,
port: port_,
);
},
@@ -4166,7 +4205,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 102,
+ funcId: 103,
port: port_,
);
},
@@ -4198,7 +4237,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 103,
+ funcId: 104,
port: port_,
);
},
@@ -4233,7 +4272,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 104,
+ funcId: 105,
port: port_,
);
},
@@ -4265,7 +4304,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 105,
+ funcId: 106,
port: port_,
);
},
@@ -4303,7 +4342,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 106,
+ funcId: 107,
port: port_,
);
},
@@ -4342,7 +4381,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 107,
+ funcId: 108,
port: port_,
);
},
@@ -4377,7 +4416,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 108,
+ funcId: 109,
port: port_,
);
},
@@ -4412,7 +4451,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 109,
+ funcId: 110,
port: port_,
);
},
@@ -4447,7 +4486,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 110,
+ funcId: 111,
port: port_,
);
},
@@ -4480,7 +4519,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
return pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 111,
+ funcId: 112,
)!;
},
codec: SseCodec(
@@ -4520,7 +4559,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 112,
+ funcId: 113,
port: port_,
);
},
@@ -4565,7 +4604,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 113,
+ funcId: 114,
port: port_,
);
},
@@ -4595,7 +4634,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 114,
+ funcId: 115,
port: port_,
);
},
@@ -4628,7 +4667,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 115,
+ funcId: 116,
port: port_,
);
},
@@ -4663,7 +4702,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
pdeCallFfi(
generalizedFrbRustBinding,
serializer,
- funcId: 116,
+ funcId: 117,
port: port_,
);
},
@@ -4995,6 +5034,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
throw UnimplementedError();
}
+ @protected
+ RustStreamSink> dco_decode_StreamSink_list_String_Sse(
+ dynamic raw,
+ ) {
+ // Codec=Dco (DartCObject based), see doc to use other codecs
+ throw UnimplementedError();
+ }
+
@protected
String dco_decode_String(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
@@ -5782,8 +5829,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
UserConfig dco_decode_user_config(dynamic raw) {
// Codec=Dco (DartCObject based), see doc to use other codecs
final arr = raw as List;
- if (arr.length != 58)
- throw Exception('unexpected arr length: expect 58 but see ${arr.length}');
+ if (arr.length != 59)
+ throw Exception('unexpected arr length: expect 59 but see ${arr.length}');
return UserConfig(
userId: dco_decode_i_64(arr[0]),
username: dco_decode_String(arr[1]),
@@ -5845,9 +5892,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
passwordLessRecovery:
dco_decode_opt_box_autoadd_passwordless_recovery_config(arr[53]),
fcmToken: dco_decode_opt_String(arr[54]),
- currentSetupPage: dco_decode_opt_String(arr[55]),
- skipSetupPages: dco_decode_bool(arr[56]),
- hasZoomed: dco_decode_bool(arr[57]),
+ lastFcmWakeupAt: dco_decode_opt_box_autoadd_i_64(arr[55]),
+ currentSetupPage: dco_decode_opt_String(arr[56]),
+ skipSetupPages: dco_decode_bool(arr[57]),
+ hasZoomed: dco_decode_bool(arr[58]),
);
}
@@ -5912,6 +5960,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
throw UnimplementedError('Unreachable ()');
}
+ @protected
+ RustStreamSink> sse_decode_StreamSink_list_String_Sse(
+ SseDeserializer deserializer,
+ ) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ throw UnimplementedError('Unreachable ()');
+ }
+
@protected
String sse_decode_String(SseDeserializer deserializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -6938,6 +6994,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
var var_passwordLessRecovery =
sse_decode_opt_box_autoadd_passwordless_recovery_config(deserializer);
var var_fcmToken = sse_decode_opt_String(deserializer);
+ var var_lastFcmWakeupAt = sse_decode_opt_box_autoadd_i_64(deserializer);
var var_currentSetupPage = sse_decode_opt_String(deserializer);
var var_skipSetupPages = sse_decode_bool(deserializer);
var var_hasZoomed = sse_decode_bool(deserializer);
@@ -7000,6 +7057,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
isBackupEnabled: var_isBackupEnabled,
passwordLessRecovery: var_passwordLessRecovery,
fcmToken: var_fcmToken,
+ lastFcmWakeupAt: var_lastFcmWakeupAt,
currentSetupPage: var_currentSetupPage,
skipSetupPages: var_skipSetupPages,
hasZoomed: var_hasZoomed,
@@ -7175,6 +7233,23 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
);
}
+ @protected
+ void sse_encode_StreamSink_list_String_Sse(
+ RustStreamSink> self,
+ SseSerializer serializer,
+ ) {
+ // Codec=Sse (Serialization based), see doc to use other codecs
+ sse_encode_String(
+ self.setupAndSerialize(
+ codec: SseCodec(
+ decodeSuccessData: sse_decode_list_String,
+ decodeErrorData: sse_decode_AnyhowException,
+ ),
+ ),
+ serializer,
+ );
+ }
+
@protected
void sse_encode_String(String self, SseSerializer serializer) {
// Codec=Sse (Serialization based), see doc to use other codecs
@@ -8112,6 +8187,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
serializer,
);
sse_encode_opt_String(self.fcmToken, serializer);
+ sse_encode_opt_box_autoadd_i_64(self.lastFcmWakeupAt, serializer);
sse_encode_opt_String(self.currentSetupPage, serializer);
sse_encode_bool(self.skipSetupPages, serializer);
sse_encode_bool(self.hasZoomed, serializer);
diff --git a/lib/core/frb_generated.io.dart b/lib/core/frb_generated.io.dart
index e94d702e..3d1c6a89 100644
--- a/lib/core/frb_generated.io.dart
+++ b/lib/core/frb_generated.io.dart
@@ -82,6 +82,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl {
@protected
RustStreamSink dco_decode_StreamSink_api_event_Sse(dynamic raw);
+ @protected
+ RustStreamSink> dco_decode_StreamSink_list_String_Sse(
+ dynamic raw,
+ );
+
@protected
String dco_decode_String(dynamic raw);
@@ -405,6 +410,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl {
SseDeserializer deserializer,
);
+ @protected
+ RustStreamSink> sse_decode_StreamSink_list_String_Sse(
+ SseDeserializer deserializer,
+ );
+
@protected
String sse_decode_String(SseDeserializer deserializer);
@@ -828,6 +838,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl {
SseSerializer serializer,
);
+ @protected
+ void sse_encode_StreamSink_list_String_Sse(
+ RustStreamSink> self,
+ SseSerializer serializer,
+ );
+
@protected
void sse_encode_String(String self, SseSerializer serializer);
diff --git a/lib/core/frb_generated.web.dart b/lib/core/frb_generated.web.dart
index 6ab4ed9c..fa5079d9 100644
--- a/lib/core/frb_generated.web.dart
+++ b/lib/core/frb_generated.web.dart
@@ -84,6 +84,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl {
@protected
RustStreamSink dco_decode_StreamSink_api_event_Sse(dynamic raw);
+ @protected
+ RustStreamSink> dco_decode_StreamSink_list_String_Sse(
+ dynamic raw,
+ );
+
@protected
String dco_decode_String(dynamic raw);
@@ -407,6 +412,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl {
SseDeserializer deserializer,
);
+ @protected
+ RustStreamSink> sse_decode_StreamSink_list_String_Sse(
+ SseDeserializer deserializer,
+ );
+
@protected
String sse_decode_String(SseDeserializer deserializer);
@@ -830,6 +840,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl {
SseSerializer serializer,
);
+ @protected
+ void sse_encode_StreamSink_list_String_Sse(
+ RustStreamSink> self,
+ SseSerializer serializer,
+ );
+
@protected
void sse_encode_String(String self, SseSerializer serializer);
diff --git a/lib/core/user_config.dart b/lib/core/user_config.dart
index 43aff654..d3fd8192 100644
--- a/lib/core/user_config.dart
+++ b/lib/core/user_config.dart
@@ -158,6 +158,11 @@ class UserConfig {
bool isBackupEnabled;
PasswordlessRecoveryConfig? passwordLessRecovery;
String? fcmToken;
+
+ /// Unix seconds of the last opaque FCM/APNs wake-up that reached the native
+ /// notification worker. Recorded in Rust because Flutter is no longer
+ /// started for background delivery on either platform.
+ PlatformInt64? lastFcmWakeupAt;
String? currentSetupPage;
bool skipSetupPages;
bool hasZoomed;
@@ -218,6 +223,7 @@ class UserConfig {
required this.isBackupEnabled,
this.passwordLessRecovery,
this.fcmToken,
+ this.lastFcmWakeupAt,
this.currentSetupPage,
required this.skipSetupPages,
required this.hasZoomed,
@@ -280,6 +286,7 @@ class UserConfig {
isBackupEnabled.hashCode ^
passwordLessRecovery.hashCode ^
fcmToken.hashCode ^
+ lastFcmWakeupAt.hashCode ^
currentSetupPage.hashCode ^
skipSetupPages.hashCode ^
hasZoomed.hashCode;
@@ -351,6 +358,7 @@ class UserConfig {
isBackupEnabled == other.isBackupEnabled &&
passwordLessRecovery == other.passwordLessRecovery &&
fcmToken == other.fcmToken &&
+ lastFcmWakeupAt == other.lastFcmWakeupAt &&
currentSetupPage == other.currentSetupPage &&
skipSetupPages == other.skipSetupPages &&
hasZoomed == other.hasZoomed;
diff --git a/lib/globals.dart b/lib/globals.dart
index 4b476b6b..45324b38 100644
--- a/lib/globals.dart
+++ b/lib/globals.dart
@@ -1,12 +1,15 @@
import 'dart:async';
+import 'dart:io';
import 'dart:math';
import 'package:camera/camera.dart';
+import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:twonly/src/utils/log.dart';
final int isolateCallbackId = Random().nextInt(0x7FFFFFFF);
class AppEnvironment {
+ static const _runtimeChannel = MethodChannel('eu.twonly/runtime_storage');
static late String cacheDir;
static late String supportDir;
@@ -18,11 +21,66 @@ class AppEnvironment {
static Future init() async {
if (_isInitialized) return;
cacheDir = (await getApplicationCacheDirectory()).path;
- supportDir = (await getApplicationSupportDirectory()).path;
+ final privateSupportDir = (await getApplicationSupportDirectory()).path;
+ if (Platform.isIOS) {
+ final sharedSupportDir = await _runtimeChannel.invokeMethod(
+ 'runtimeSupportDirectory',
+ );
+ if (sharedSupportDir == null || sharedSupportDir.isEmpty) {
+ throw StateError('The iOS runtime App Group is unavailable.');
+ }
+ await _migrateToSharedSupportDirectory(
+ Directory(privateSupportDir),
+ Directory(sharedSupportDir),
+ );
+ supportDir = sharedSupportDir;
+ } else {
+ supportDir = privateSupportDir;
+ }
Log.init();
_isInitialized = true;
}
+ static Future _migrateToSharedSupportDirectory(
+ Directory source,
+ Directory destination,
+ ) async {
+ await destination.create(recursive: true);
+ final marker = File('${destination.path}/.runtime_storage_migrated_v1');
+ if (await marker.exists() || !await source.exists()) return;
+
+ await for (final entity in source.list(followLinks: false)) {
+ await _copyEntity(entity, destination.path);
+ }
+ await marker.writeAsString(
+ DateTime.now().toUtc().toIso8601String(),
+ flush: true,
+ );
+ }
+
+ static Future _copyEntity(
+ FileSystemEntity entity,
+ String destinationDirectory,
+ ) async {
+ final name = entity.uri.pathSegments.where((value) => value.isNotEmpty).last;
+ final destinationPath = '$destinationDirectory/$name';
+ if (entity is Directory) {
+ final destination = Directory(destinationPath);
+ await destination.create(recursive: true);
+ await for (final child in entity.list(followLinks: false)) {
+ await _copyEntity(child, destination.path);
+ }
+ return;
+ }
+ if (entity is! File) return;
+
+ final destination = File(destinationPath);
+ if (await destination.exists()) return;
+ final temporary = File('$destinationPath.migrating');
+ await entity.copy(temporary.path);
+ await temporary.rename(destination.path);
+ }
+
static void initTesting({String? customCacheDir, String? customSupportDir}) {
cacheDir = customCacheDir ?? '/tmp/twonly_cache';
supportDir = customSupportDir ?? '/tmp/twonly_support';
diff --git a/lib/main.dart b/lib/main.dart
index cc8fdfbf..c24a9988 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -28,6 +28,7 @@ import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
import 'package:twonly/src/services/memories/memories.service.dart';
import 'package:twonly/src/services/migrations.service.dart';
import 'package:twonly/src/services/notifications/fcm.notifications.dart';
+import 'package:twonly/src/services/notifications/native.notifications.dart';
import 'package:twonly/src/services/notifications/setup.notifications.dart';
import 'package:twonly/src/utils/avatars.dart';
import 'package:twonly/src/utils/exclusive_access.utils.dart';
@@ -102,6 +103,7 @@ void main() async {
var storageError = await twonlyMinimumInitialization();
await FcmNotificationService.initStartup();
await setupPushNotification();
+ NativeNotificationService.init();
var userExists = false;
diff --git a/lib/src/callbacks/logging.callbacks.dart b/lib/src/callbacks/logging.callbacks.dart
index 6b1a9c67..8f5a3c40 100644
--- a/lib/src/callbacks/logging.callbacks.dart
+++ b/lib/src/callbacks/logging.callbacks.dart
@@ -1,34 +1,73 @@
import 'dart:async';
-import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
+import 'package:logging/logging.dart';
import 'package:twonly/src/utils/log.dart';
+/// Matches ANSI escape sequences (CSI sequences, caret notation like ^[[3m or \^[[3m).
+final _ansiRegex = RegExp(r'(?:\x1B|\\?\^\[)\[[0-?]*[ -/]*[@-~]');
+
+/// Matches the plain `ShortEventFormatter` output from `rust/src/log.rs`:
+/// `HH:MM:SS LEVEL dir/file.rs:12 `.
+final _rustLogLine = RegExp(
+ r'^\d{2}:\d{2}:\d{2} (TRACE|DEBUG|INFO|WARN|ERROR) +(\S+:\d+) ?(.*)$',
+ dotAll: true,
+);
+
+/// Rust levels mapped onto the `logging` levels the Dart side already prints.
+/// `SHOUT` is what [Log.error] uses, so a Rust `ERROR` reads the same as a Dart
+/// one.
+const Map _levels = {
+ 'TRACE': Level.FINEST,
+ 'DEBUG': Level.FINE,
+ 'INFO': Level.INFO,
+ 'WARN': Level.WARNING,
+ 'ERROR': Level.SHOUT,
+};
+
class LoggingCallbacks {
static Future> getStreamSink() async {
final dartLogSink = RustStreamSink();
+ // `stream` throws until flutter_rust_bridge has serialized the sink for
+ // Rust, which only happens once this function has returned. Poll for it
+ // instead of racing; buffered events are replayed on the first listen.
+ var attempts = 0;
Timer.periodic(const Duration(milliseconds: 100), (timer) {
+ attempts++;
try {
- dartLogSink.stream.listen(
- (log) {
- if (log.contains('INFO ')) {
- Log.info(log.split('INFO ')[1]);
- } else if (log.contains('DEBUG ')) {
- Log.info(log.split('DEBUG ')[1]);
- } else if (kDebugMode &&
- !Platform.environment.containsKey('FLUTTER_TEST')) {
- // ignore: avoid_print
- print(log);
- }
- },
- );
+ dartLogSink.stream.listen(_handleRustLog);
timer.cancel();
- } catch (e) {
- // stream not yet initialized
+ } catch (_) {
+ // Stream not yet initialized.
+ if (attempts >= 100) {
+ timer.cancel();
+ Log.warn('Rust log sink never became available.');
+ }
}
});
return dartLogSink;
}
+
+ @visibleForTesting
+ static void handleRustLog(String log) => _handleRustLog(log);
+
+ static void _handleRustLog(String log) {
+ final sanitizedLog = log.replaceAll(_ansiRegex, '');
+ final match = _rustLogLine.firstMatch(sanitizedLog);
+ if (match == null) {
+ // Not a formatted event (panic output, a continuation line, ...).
+ Log.warn(sanitizedLog);
+ return;
+ }
+
+ // The level and the `file.rs:line` origin belong in the record itself, not
+ // repeated inside the message: the Dart call site here says nothing useful.
+ Log.forward(
+ level: _levels[match.group(1)] ?? Level.INFO,
+ source: match.group(2)!,
+ messageInput: match.group(3),
+ );
+ }
}
diff --git a/lib/src/constants/secure_storage.keys.dart b/lib/src/constants/secure_storage.keys.dart
index d8020454..a6da571c 100644
--- a/lib/src/constants/secure_storage.keys.dart
+++ b/lib/src/constants/secure_storage.keys.dart
@@ -10,9 +10,6 @@ class SecureStorageKeys {
static const String userData = 'userData';
// Not required for backup...
- static const String receivingPushKeys = 'push_keys_receiving';
- static const String sendingPushKeys = 'push_keys_sending';
- static const String lastFcmMessageTimestamp = 'last_fcm_message_timestamp';
static const String lastServerMessageTimestamp =
'last_server_message_timestamp';
}
diff --git a/lib/src/database/rust_change_notifier.dart b/lib/src/database/rust_change_notifier.dart
new file mode 100644
index 00000000..28705ffb
--- /dev/null
+++ b/lib/src/database/rust_change_notifier.dart
@@ -0,0 +1,38 @@
+import 'dart:async';
+
+import 'package:drift/drift.dart';
+import 'package:twonly/core/bridge/wrapper/app_database.dart';
+import 'package:twonly/src/utils/log.dart';
+
+/// Bridges Rust-side commits into Drift's stream query invalidation.
+///
+/// Rust owns the SQLite connection. Statements Drift issues go through
+/// `openRustAppDatabase` and invalidate its query streams as usual, but every
+/// write Rust performs on its own bypasses that executor entirely — Drift never
+/// learns about it, so `watch()` keeps serving stale rows and the UI does not
+/// update until something else happens to touch the same table.
+///
+/// Rust already broadcasts the tables it commits to (`notify_committed`); this
+/// forwards those batches into [GeneratedDatabase.notifyUpdates].
+StreamSubscription> listenToRustDatabaseChanges(
+ GeneratedDatabase db,
+) {
+ return RustAppDatabase.changes().listen(
+ (tables) {
+ // An empty batch means Rust could not tell us precisely what changed
+ // (fresh subscription, or dropped notifications). Invalidate everything
+ // rather than leaving the UI stale.
+ final updates = tables.isEmpty
+ ? db.allTables.map(TableUpdate.onTable).toSet()
+ : tables.map(TableUpdate.new).toSet();
+ db.notifyUpdates(updates);
+ },
+ onError: (Object error, StackTrace stackTrace) {
+ Log.error(
+ 'Rust database change stream failed',
+ error: error,
+ stackTrace: stackTrace,
+ );
+ },
+ );
+}
diff --git a/lib/src/database/twonly.db.dart b/lib/src/database/twonly.db.dart
index 40eea8f7..74d0c6ce 100644
--- a/lib/src/database/twonly.db.dart
+++ b/lib/src/database/twonly.db.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
import 'package:drift/drift.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
import 'package:twonly/src/database/daos/groups.dao.dart';
@@ -9,6 +11,7 @@ import 'package:twonly/src/database/daos/reactions.dao.dart';
import 'package:twonly/src/database/daos/receipts.dao.dart';
import 'package:twonly/src/database/daos/shortcuts.dao.dart';
import 'package:twonly/src/database/daos/user_discovery.dao.dart';
+import 'package:twonly/src/database/rust_change_notifier.dart';
import 'package:twonly/src/database/rust_query_executor.dart';
import 'package:twonly/src/database/tables/contacts.table.dart';
import 'package:twonly/src/database/tables/groups.table.dart';
@@ -64,14 +67,27 @@ part 'twonly.db.g.dart';
],
)
class TwonlyDB extends _$TwonlyDB {
- TwonlyDB([QueryExecutor? e])
- : super(
- e ?? openRustAppDatabase(),
- );
+ TwonlyDB([QueryExecutor? e]) : super(e ?? openRustAppDatabase()) {
+ // Only the Rust-backed connection needs external change notifications.
+ // An explicit executor is a plain Drift-owned database (tests, and the
+ // legacy import in main.dart), where Drift already sees every write.
+ if (e == null) {
+ _rustChanges = listenToRustDatabaseChanges(this);
+ }
+ }
// ignore: matching_super_parameters
TwonlyDB.forTesting(DatabaseConnection super.connection);
+ StreamSubscription>? _rustChanges;
+
+ @override
+ Future close() async {
+ await _rustChanges?.cancel();
+ _rustChanges = null;
+ return super.close();
+ }
+
@override
int get schemaVersion => 25;
diff --git a/lib/src/model/protobuf/client/generated/http_requests.pb.dart b/lib/src/model/protobuf/client/generated/http_requests.pb.dart
index 768ce32b..633eca93 100644
--- a/lib/src/model/protobuf/client/generated/http_requests.pb.dart
+++ b/lib/src/model/protobuf/client/generated/http_requests.pb.dart
@@ -21,12 +21,12 @@ class TextMessage extends $pb.GeneratedMessage {
factory TextMessage({
$fixnum.Int64? userId,
$core.List<$core.int>? body,
- $core.List<$core.int>? pushData,
+ $core.bool? wakeReceiver,
}) {
final result = create();
if (userId != null) result.userId = userId;
if (body != null) result.body = body;
- if (pushData != null) result.pushData = pushData;
+ if (wakeReceiver != null) result.wakeReceiver = wakeReceiver;
return result;
}
@@ -46,8 +46,7 @@ class TextMessage extends $pb.GeneratedMessage {
..aInt64(1, _omitFieldNames ? '' : 'userId')
..a<$core.List<$core.int>>(
2, _omitFieldNames ? '' : 'body', $pb.PbFieldType.OY)
- ..a<$core.List<$core.int>>(
- 3, _omitFieldNames ? '' : 'pushData', $pb.PbFieldType.OY)
+ ..aOB(4, _omitFieldNames ? '' : 'wakeReceiver')
..hasRequiredFields = false;
@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
@@ -87,14 +86,14 @@ class TextMessage extends $pb.GeneratedMessage {
@$pb.TagNumber(2)
void clearBody() => $_clearField(2);
- @$pb.TagNumber(3)
- $core.List<$core.int> get pushData => $_getN(2);
- @$pb.TagNumber(3)
- set pushData($core.List<$core.int> value) => $_setBytes(2, value);
- @$pb.TagNumber(3)
- $core.bool hasPushData() => $_has(2);
- @$pb.TagNumber(3)
- void clearPushData() => $_clearField(3);
+ @$pb.TagNumber(4)
+ $core.bool get wakeReceiver => $_getBF(2);
+ @$pb.TagNumber(4)
+ set wakeReceiver($core.bool value) => $_setBool(2, value);
+ @$pb.TagNumber(4)
+ $core.bool hasWakeReceiver() => $_has(2);
+ @$pb.TagNumber(4)
+ void clearWakeReceiver() => $_clearField(4);
}
class UploadRequest extends $pb.GeneratedMessage {
diff --git a/lib/src/model/protobuf/client/generated/http_requests.pbjson.dart b/lib/src/model/protobuf/client/generated/http_requests.pbjson.dart
index eef1036e..b606a565 100644
--- a/lib/src/model/protobuf/client/generated/http_requests.pbjson.dart
+++ b/lib/src/model/protobuf/client/generated/http_requests.pbjson.dart
@@ -21,25 +21,17 @@ const TextMessage$json = {
'2': [
{'1': 'user_id', '3': 1, '4': 1, '5': 3, '10': 'userId'},
{'1': 'body', '3': 2, '4': 1, '5': 12, '10': 'body'},
- {
- '1': 'push_data',
- '3': 3,
- '4': 1,
- '5': 12,
- '9': 0,
- '10': 'pushData',
- '17': true
- },
+ {'1': 'wake_receiver', '3': 4, '4': 1, '5': 8, '10': 'wakeReceiver'},
],
- '8': [
- {'1': '_push_data'},
+ '9': [
+ {'1': 3, '2': 4},
],
};
/// Descriptor for `TextMessage`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List textMessageDescriptor = $convert.base64Decode(
'CgtUZXh0TWVzc2FnZRIXCgd1c2VyX2lkGAEgASgDUgZ1c2VySWQSEgoEYm9keRgCIAEoDFIEYm'
- '9keRIgCglwdXNoX2RhdGEYAyABKAxIAFIIcHVzaERhdGGIAQFCDAoKX3B1c2hfZGF0YQ==');
+ '9keRIjCg13YWtlX3JlY2VpdmVyGAQgASgIUgx3YWtlUmVjZWl2ZXJKBAgDEAQ=');
@$core.Deprecated('Use uploadRequestDescriptor instead')
const UploadRequest$json = {
diff --git a/lib/src/model/protobuf/client/generated/messages.pb.dart b/lib/src/model/protobuf/client/generated/messages.pb.dart
index 6c7043fc..56305f9e 100644
--- a/lib/src/model/protobuf/client/generated/messages.pb.dart
+++ b/lib/src/model/protobuf/client/generated/messages.pb.dart
@@ -1140,98 +1140,6 @@ class EncryptedContent_ContactUpdate extends $pb.GeneratedMessage {
void clearDisplayName() => $_clearField(4);
}
-class EncryptedContent_PushKeys extends $pb.GeneratedMessage {
- factory EncryptedContent_PushKeys({
- EncryptedContent_PushKeys_Type? type,
- $fixnum.Int64? keyId,
- $core.List<$core.int>? key,
- $fixnum.Int64? createdAt,
- }) {
- final result = create();
- if (type != null) result.type = type;
- if (keyId != null) result.keyId = keyId;
- if (key != null) result.key = key;
- if (createdAt != null) result.createdAt = createdAt;
- return result;
- }
-
- EncryptedContent_PushKeys._();
-
- factory EncryptedContent_PushKeys.fromBuffer($core.List<$core.int> data,
- [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(data, registry);
- factory EncryptedContent_PushKeys.fromJson($core.String json,
- [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(json, registry);
-
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- _omitMessageNames ? '' : 'EncryptedContent.PushKeys',
- createEmptyInstance: create)
- ..aE(1, _omitFieldNames ? '' : 'type',
- enumValues: EncryptedContent_PushKeys_Type.values)
- ..aInt64(2, _omitFieldNames ? '' : 'keyId')
- ..a<$core.List<$core.int>>(
- 3, _omitFieldNames ? '' : 'key', $pb.PbFieldType.OY)
- ..aInt64(4, _omitFieldNames ? '' : 'createdAt')
- ..hasRequiredFields = false;
-
- @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
- EncryptedContent_PushKeys clone() => deepCopy();
- @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
- EncryptedContent_PushKeys copyWith(
- void Function(EncryptedContent_PushKeys) updates) =>
- super.copyWith((message) => updates(message as EncryptedContent_PushKeys))
- as EncryptedContent_PushKeys;
-
- @$core.override
- $pb.BuilderInfo get info_ => _i;
-
- @$core.pragma('dart2js:noInline')
- static EncryptedContent_PushKeys create() => EncryptedContent_PushKeys._();
- @$core.override
- EncryptedContent_PushKeys createEmptyInstance() => create();
- @$core.pragma('dart2js:noInline')
- static EncryptedContent_PushKeys getDefault() => _defaultInstance ??=
- $pb.GeneratedMessage.$_defaultFor(create);
- static EncryptedContent_PushKeys? _defaultInstance;
-
- @$pb.TagNumber(1)
- EncryptedContent_PushKeys_Type get type => $_getN(0);
- @$pb.TagNumber(1)
- set type(EncryptedContent_PushKeys_Type value) => $_setField(1, value);
- @$pb.TagNumber(1)
- $core.bool hasType() => $_has(0);
- @$pb.TagNumber(1)
- void clearType() => $_clearField(1);
-
- @$pb.TagNumber(2)
- $fixnum.Int64 get keyId => $_getI64(1);
- @$pb.TagNumber(2)
- set keyId($fixnum.Int64 value) => $_setInt64(1, value);
- @$pb.TagNumber(2)
- $core.bool hasKeyId() => $_has(1);
- @$pb.TagNumber(2)
- void clearKeyId() => $_clearField(2);
-
- @$pb.TagNumber(3)
- $core.List<$core.int> get key => $_getN(2);
- @$pb.TagNumber(3)
- set key($core.List<$core.int> value) => $_setBytes(2, value);
- @$pb.TagNumber(3)
- $core.bool hasKey() => $_has(2);
- @$pb.TagNumber(3)
- void clearKey() => $_clearField(3);
-
- @$pb.TagNumber(4)
- $fixnum.Int64 get createdAt => $_getI64(3);
- @$pb.TagNumber(4)
- set createdAt($fixnum.Int64 value) => $_setInt64(3, value);
- @$pb.TagNumber(4)
- $core.bool hasCreatedAt() => $_has(3);
- @$pb.TagNumber(4)
- void clearCreatedAt() => $_clearField(4);
-}
-
class EncryptedContent_FlameSync extends $pb.GeneratedMessage {
factory EncryptedContent_FlameSync({
$fixnum.Int64? flameCounter,
@@ -1724,7 +1632,6 @@ class EncryptedContent extends $pb.GeneratedMessage {
EncryptedContent_ContactUpdate? contactUpdate,
EncryptedContent_ContactRequest? contactRequest,
EncryptedContent_FlameSync? flameSync,
- EncryptedContent_PushKeys? pushKeys,
EncryptedContent_Reaction? reaction,
EncryptedContent_TextMessage? textMessage,
EncryptedContent_GroupCreate? groupCreate,
@@ -1754,7 +1661,6 @@ class EncryptedContent extends $pb.GeneratedMessage {
if (contactUpdate != null) result.contactUpdate = contactUpdate;
if (contactRequest != null) result.contactRequest = contactRequest;
if (flameSync != null) result.flameSync = flameSync;
- if (pushKeys != null) result.pushKeys = pushKeys;
if (reaction != null) result.reaction = reaction;
if (textMessage != null) result.textMessage = textMessage;
if (groupCreate != null) result.groupCreate = groupCreate;
@@ -1813,8 +1719,6 @@ class EncryptedContent extends $pb.GeneratedMessage {
subBuilder: EncryptedContent_ContactRequest.create)
..aOM(10, _omitFieldNames ? '' : 'flameSync',
subBuilder: EncryptedContent_FlameSync.create)
- ..aOM(11, _omitFieldNames ? '' : 'pushKeys',
- subBuilder: EncryptedContent_PushKeys.create)
..aOM(12, _omitFieldNames ? '' : 'reaction',
subBuilder: EncryptedContent_Reaction.create)
..aOM(
@@ -1976,208 +1880,197 @@ class EncryptedContent extends $pb.GeneratedMessage {
@$pb.TagNumber(10)
EncryptedContent_FlameSync ensureFlameSync() => $_ensure(8);
- @$pb.TagNumber(11)
- EncryptedContent_PushKeys get pushKeys => $_getN(9);
- @$pb.TagNumber(11)
- set pushKeys(EncryptedContent_PushKeys value) => $_setField(11, value);
- @$pb.TagNumber(11)
- $core.bool hasPushKeys() => $_has(9);
- @$pb.TagNumber(11)
- void clearPushKeys() => $_clearField(11);
- @$pb.TagNumber(11)
- EncryptedContent_PushKeys ensurePushKeys() => $_ensure(9);
-
@$pb.TagNumber(12)
- EncryptedContent_Reaction get reaction => $_getN(10);
+ EncryptedContent_Reaction get reaction => $_getN(9);
@$pb.TagNumber(12)
set reaction(EncryptedContent_Reaction value) => $_setField(12, value);
@$pb.TagNumber(12)
- $core.bool hasReaction() => $_has(10);
+ $core.bool hasReaction() => $_has(9);
@$pb.TagNumber(12)
void clearReaction() => $_clearField(12);
@$pb.TagNumber(12)
- EncryptedContent_Reaction ensureReaction() => $_ensure(10);
+ EncryptedContent_Reaction ensureReaction() => $_ensure(9);
@$pb.TagNumber(13)
- EncryptedContent_TextMessage get textMessage => $_getN(11);
+ EncryptedContent_TextMessage get textMessage => $_getN(10);
@$pb.TagNumber(13)
set textMessage(EncryptedContent_TextMessage value) => $_setField(13, value);
@$pb.TagNumber(13)
- $core.bool hasTextMessage() => $_has(11);
+ $core.bool hasTextMessage() => $_has(10);
@$pb.TagNumber(13)
void clearTextMessage() => $_clearField(13);
@$pb.TagNumber(13)
- EncryptedContent_TextMessage ensureTextMessage() => $_ensure(11);
+ EncryptedContent_TextMessage ensureTextMessage() => $_ensure(10);
@$pb.TagNumber(14)
- EncryptedContent_GroupCreate get groupCreate => $_getN(12);
+ EncryptedContent_GroupCreate get groupCreate => $_getN(11);
@$pb.TagNumber(14)
set groupCreate(EncryptedContent_GroupCreate value) => $_setField(14, value);
@$pb.TagNumber(14)
- $core.bool hasGroupCreate() => $_has(12);
+ $core.bool hasGroupCreate() => $_has(11);
@$pb.TagNumber(14)
void clearGroupCreate() => $_clearField(14);
@$pb.TagNumber(14)
- EncryptedContent_GroupCreate ensureGroupCreate() => $_ensure(12);
+ EncryptedContent_GroupCreate ensureGroupCreate() => $_ensure(11);
@$pb.TagNumber(15)
- EncryptedContent_GroupJoin get groupJoin => $_getN(13);
+ EncryptedContent_GroupJoin get groupJoin => $_getN(12);
@$pb.TagNumber(15)
set groupJoin(EncryptedContent_GroupJoin value) => $_setField(15, value);
@$pb.TagNumber(15)
- $core.bool hasGroupJoin() => $_has(13);
+ $core.bool hasGroupJoin() => $_has(12);
@$pb.TagNumber(15)
void clearGroupJoin() => $_clearField(15);
@$pb.TagNumber(15)
- EncryptedContent_GroupJoin ensureGroupJoin() => $_ensure(13);
+ EncryptedContent_GroupJoin ensureGroupJoin() => $_ensure(12);
@$pb.TagNumber(16)
- EncryptedContent_GroupUpdate get groupUpdate => $_getN(14);
+ EncryptedContent_GroupUpdate get groupUpdate => $_getN(13);
@$pb.TagNumber(16)
set groupUpdate(EncryptedContent_GroupUpdate value) => $_setField(16, value);
@$pb.TagNumber(16)
- $core.bool hasGroupUpdate() => $_has(14);
+ $core.bool hasGroupUpdate() => $_has(13);
@$pb.TagNumber(16)
void clearGroupUpdate() => $_clearField(16);
@$pb.TagNumber(16)
- EncryptedContent_GroupUpdate ensureGroupUpdate() => $_ensure(14);
+ EncryptedContent_GroupUpdate ensureGroupUpdate() => $_ensure(13);
@$pb.TagNumber(17)
- EncryptedContent_ResendGroupPublicKey get resendGroupPublicKey => $_getN(15);
+ EncryptedContent_ResendGroupPublicKey get resendGroupPublicKey => $_getN(14);
@$pb.TagNumber(17)
set resendGroupPublicKey(EncryptedContent_ResendGroupPublicKey value) =>
$_setField(17, value);
@$pb.TagNumber(17)
- $core.bool hasResendGroupPublicKey() => $_has(15);
+ $core.bool hasResendGroupPublicKey() => $_has(14);
@$pb.TagNumber(17)
void clearResendGroupPublicKey() => $_clearField(17);
@$pb.TagNumber(17)
EncryptedContent_ResendGroupPublicKey ensureResendGroupPublicKey() =>
- $_ensure(15);
+ $_ensure(14);
@$pb.TagNumber(18)
- EncryptedContent_ErrorMessages get errorMessages => $_getN(16);
+ EncryptedContent_ErrorMessages get errorMessages => $_getN(15);
@$pb.TagNumber(18)
set errorMessages(EncryptedContent_ErrorMessages value) =>
$_setField(18, value);
@$pb.TagNumber(18)
- $core.bool hasErrorMessages() => $_has(16);
+ $core.bool hasErrorMessages() => $_has(15);
@$pb.TagNumber(18)
void clearErrorMessages() => $_clearField(18);
@$pb.TagNumber(18)
- EncryptedContent_ErrorMessages ensureErrorMessages() => $_ensure(16);
+ EncryptedContent_ErrorMessages ensureErrorMessages() => $_ensure(15);
@$pb.TagNumber(19)
EncryptedContent_AdditionalDataMessage get additionalDataMessage =>
- $_getN(17);
+ $_getN(16);
@$pb.TagNumber(19)
set additionalDataMessage(EncryptedContent_AdditionalDataMessage value) =>
$_setField(19, value);
@$pb.TagNumber(19)
- $core.bool hasAdditionalDataMessage() => $_has(17);
+ $core.bool hasAdditionalDataMessage() => $_has(16);
@$pb.TagNumber(19)
void clearAdditionalDataMessage() => $_clearField(19);
@$pb.TagNumber(19)
EncryptedContent_AdditionalDataMessage ensureAdditionalDataMessage() =>
- $_ensure(17);
+ $_ensure(16);
@$pb.TagNumber(20)
- EncryptedContent_TypingIndicator get typingIndicator => $_getN(18);
+ EncryptedContent_TypingIndicator get typingIndicator => $_getN(17);
@$pb.TagNumber(20)
set typingIndicator(EncryptedContent_TypingIndicator value) =>
$_setField(20, value);
@$pb.TagNumber(20)
- $core.bool hasTypingIndicator() => $_has(18);
+ $core.bool hasTypingIndicator() => $_has(17);
@$pb.TagNumber(20)
void clearTypingIndicator() => $_clearField(20);
@$pb.TagNumber(20)
- EncryptedContent_TypingIndicator ensureTypingIndicator() => $_ensure(18);
+ EncryptedContent_TypingIndicator ensureTypingIndicator() => $_ensure(17);
@$pb.TagNumber(21)
- $core.List<$core.int> get senderUserDiscoveryVersion => $_getN(19);
+ $core.List<$core.int> get senderUserDiscoveryVersion => $_getN(18);
@$pb.TagNumber(21)
set senderUserDiscoveryVersion($core.List<$core.int> value) =>
- $_setBytes(19, value);
+ $_setBytes(18, value);
@$pb.TagNumber(21)
- $core.bool hasSenderUserDiscoveryVersion() => $_has(19);
+ $core.bool hasSenderUserDiscoveryVersion() => $_has(18);
@$pb.TagNumber(21)
void clearSenderUserDiscoveryVersion() => $_clearField(21);
@$pb.TagNumber(22)
- EncryptedContent_UserDiscoveryRequest get userDiscoveryRequest => $_getN(20);
+ EncryptedContent_UserDiscoveryRequest get userDiscoveryRequest => $_getN(19);
@$pb.TagNumber(22)
set userDiscoveryRequest(EncryptedContent_UserDiscoveryRequest value) =>
$_setField(22, value);
@$pb.TagNumber(22)
- $core.bool hasUserDiscoveryRequest() => $_has(20);
+ $core.bool hasUserDiscoveryRequest() => $_has(19);
@$pb.TagNumber(22)
void clearUserDiscoveryRequest() => $_clearField(22);
@$pb.TagNumber(22)
EncryptedContent_UserDiscoveryRequest ensureUserDiscoveryRequest() =>
- $_ensure(20);
+ $_ensure(19);
@$pb.TagNumber(23)
- EncryptedContent_UserDiscoveryUpdate get userDiscoveryUpdate => $_getN(21);
+ EncryptedContent_UserDiscoveryUpdate get userDiscoveryUpdate => $_getN(20);
@$pb.TagNumber(23)
set userDiscoveryUpdate(EncryptedContent_UserDiscoveryUpdate value) =>
$_setField(23, value);
@$pb.TagNumber(23)
- $core.bool hasUserDiscoveryUpdate() => $_has(21);
+ $core.bool hasUserDiscoveryUpdate() => $_has(20);
@$pb.TagNumber(23)
void clearUserDiscoveryUpdate() => $_clearField(23);
@$pb.TagNumber(23)
EncryptedContent_UserDiscoveryUpdate ensureUserDiscoveryUpdate() =>
- $_ensure(21);
+ $_ensure(20);
@$pb.TagNumber(24)
- EncryptedContent_KeyVerificationProof get keyVerificationProof => $_getN(22);
+ EncryptedContent_KeyVerificationProof get keyVerificationProof => $_getN(21);
@$pb.TagNumber(24)
set keyVerificationProof(EncryptedContent_KeyVerificationProof value) =>
$_setField(24, value);
@$pb.TagNumber(24)
- $core.bool hasKeyVerificationProof() => $_has(22);
+ $core.bool hasKeyVerificationProof() => $_has(21);
@$pb.TagNumber(24)
void clearKeyVerificationProof() => $_clearField(24);
@$pb.TagNumber(24)
EncryptedContent_KeyVerificationProof ensureKeyVerificationProof() =>
- $_ensure(22);
+ $_ensure(21);
@$pb.TagNumber(25)
- $core.bool get askForFriendPromotions => $_getBF(23);
+ $core.bool get askForFriendPromotions => $_getBF(22);
@$pb.TagNumber(25)
- set askForFriendPromotions($core.bool value) => $_setBool(23, value);
+ set askForFriendPromotions($core.bool value) => $_setBool(22, value);
@$pb.TagNumber(25)
- $core.bool hasAskForFriendPromotions() => $_has(23);
+ $core.bool hasAskForFriendPromotions() => $_has(22);
@$pb.TagNumber(25)
void clearAskForFriendPromotions() => $_clearField(25);
@$pb.TagNumber(26)
- EncryptedContent_PasswordLessRecovery get passwordlessRecovery => $_getN(24);
+ EncryptedContent_PasswordLessRecovery get passwordlessRecovery => $_getN(23);
@$pb.TagNumber(26)
set passwordlessRecovery(EncryptedContent_PasswordLessRecovery value) =>
$_setField(26, value);
@$pb.TagNumber(26)
- $core.bool hasPasswordlessRecovery() => $_has(24);
+ $core.bool hasPasswordlessRecovery() => $_has(23);
@$pb.TagNumber(26)
void clearPasswordlessRecovery() => $_clearField(26);
@$pb.TagNumber(26)
EncryptedContent_PasswordLessRecovery ensurePasswordlessRecovery() =>
- $_ensure(24);
+ $_ensure(23);
@$pb.TagNumber(27)
EncryptedContent_PasswordLessRecoveryHeartbeat
- get passwordlessRecoveryHeartbeat => $_getN(25);
+ get passwordlessRecoveryHeartbeat => $_getN(24);
@$pb.TagNumber(27)
set passwordlessRecoveryHeartbeat(
EncryptedContent_PasswordLessRecoveryHeartbeat value) =>
$_setField(27, value);
@$pb.TagNumber(27)
- $core.bool hasPasswordlessRecoveryHeartbeat() => $_has(25);
+ $core.bool hasPasswordlessRecoveryHeartbeat() => $_has(24);
@$pb.TagNumber(27)
void clearPasswordlessRecoveryHeartbeat() => $_clearField(27);
@$pb.TagNumber(27)
EncryptedContent_PasswordLessRecoveryHeartbeat
- ensurePasswordlessRecoveryHeartbeat() => $_ensure(25);
+ ensurePasswordlessRecoveryHeartbeat() => $_ensure(24);
}
const $core.bool _omitFieldNames =
diff --git a/lib/src/model/protobuf/client/generated/messages.pbenum.dart b/lib/src/model/protobuf/client/generated/messages.pbenum.dart
index 7ff4b706..d98c74c6 100644
--- a/lib/src/model/protobuf/client/generated/messages.pbenum.dart
+++ b/lib/src/model/protobuf/client/generated/messages.pbenum.dart
@@ -169,25 +169,5 @@ class EncryptedContent_ContactUpdate_Type extends $pb.ProtobufEnum {
const EncryptedContent_ContactUpdate_Type._(super.value, super.name);
}
-class EncryptedContent_PushKeys_Type extends $pb.ProtobufEnum {
- static const EncryptedContent_PushKeys_Type REQUEST =
- EncryptedContent_PushKeys_Type._(0, _omitEnumNames ? '' : 'REQUEST');
- static const EncryptedContent_PushKeys_Type UPDATE =
- EncryptedContent_PushKeys_Type._(1, _omitEnumNames ? '' : 'UPDATE');
-
- static const $core.List values =
- [
- REQUEST,
- UPDATE,
- ];
-
- static final $core.List _byValue =
- $pb.ProtobufEnum.$_initByValueList(values, 1);
- static EncryptedContent_PushKeys_Type? valueOf($core.int value) =>
- value < 0 || value >= _byValue.length ? null : _byValue[value];
-
- const EncryptedContent_PushKeys_Type._(super.value, super.name);
-}
-
const $core.bool _omitEnumNames =
$core.bool.fromEnvironment('protobuf.omit_enum_names');
diff --git a/lib/src/model/protobuf/client/generated/messages.pbjson.dart b/lib/src/model/protobuf/client/generated/messages.pbjson.dart
index 51481a50..41adb98a 100644
--- a/lib/src/model/protobuf/client/generated/messages.pbjson.dart
+++ b/lib/src/model/protobuf/client/generated/messages.pbjson.dart
@@ -124,23 +124,13 @@ const EncryptedContent$json = {
'10': 'flameSync',
'17': true
},
- {
- '1': 'push_keys',
- '3': 11,
- '4': 1,
- '5': 11,
- '6': '.EncryptedContent.PushKeys',
- '9': 11,
- '10': 'pushKeys',
- '17': true
- },
{
'1': 'reaction',
'3': 12,
'4': 1,
'5': 11,
'6': '.EncryptedContent.Reaction',
- '9': 12,
+ '9': 11,
'10': 'reaction',
'17': true
},
@@ -150,7 +140,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.TextMessage',
- '9': 13,
+ '9': 12,
'10': 'textMessage',
'17': true
},
@@ -160,7 +150,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.GroupCreate',
- '9': 14,
+ '9': 13,
'10': 'groupCreate',
'17': true
},
@@ -170,7 +160,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.GroupJoin',
- '9': 15,
+ '9': 14,
'10': 'groupJoin',
'17': true
},
@@ -180,7 +170,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.GroupUpdate',
- '9': 16,
+ '9': 15,
'10': 'groupUpdate',
'17': true
},
@@ -190,7 +180,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.ResendGroupPublicKey',
- '9': 17,
+ '9': 16,
'10': 'resendGroupPublicKey',
'17': true
},
@@ -200,7 +190,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.ErrorMessages',
- '9': 18,
+ '9': 17,
'10': 'errorMessages',
'17': true
},
@@ -210,7 +200,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.AdditionalDataMessage',
- '9': 19,
+ '9': 18,
'10': 'additionalDataMessage',
'17': true
},
@@ -220,7 +210,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.TypingIndicator',
- '9': 20,
+ '9': 19,
'10': 'typingIndicator',
'17': true
},
@@ -230,7 +220,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.UserDiscoveryRequest',
- '9': 21,
+ '9': 20,
'10': 'userDiscoveryRequest',
'17': true
},
@@ -240,7 +230,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.UserDiscoveryUpdate',
- '9': 22,
+ '9': 21,
'10': 'userDiscoveryUpdate',
'17': true
},
@@ -250,7 +240,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.KeyVerificationProof',
- '9': 23,
+ '9': 22,
'10': 'keyVerificationProof',
'17': true
},
@@ -260,7 +250,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.PasswordLessRecovery',
- '9': 24,
+ '9': 23,
'10': 'passwordlessRecovery',
'17': true
},
@@ -270,7 +260,7 @@ const EncryptedContent$json = {
'4': 1,
'5': 11,
'6': '.EncryptedContent.PasswordLessRecoveryHeartbeat',
- '9': 25,
+ '9': 24,
'10': 'passwordlessRecoveryHeartbeat',
'17': true
},
@@ -289,7 +279,6 @@ const EncryptedContent$json = {
EncryptedContent_MediaUpdate$json,
EncryptedContent_ContactRequest$json,
EncryptedContent_ContactUpdate$json,
- EncryptedContent_PushKeys$json,
EncryptedContent_FlameSync$json,
EncryptedContent_TypingIndicator$json,
EncryptedContent_UserDiscoveryRequest$json,
@@ -310,7 +299,6 @@ const EncryptedContent$json = {
{'1': '_contact_update'},
{'1': '_contact_request'},
{'1': '_flame_sync'},
- {'1': '_push_keys'},
{'1': '_reaction'},
{'1': '_text_message'},
{'1': '_group_create'},
@@ -757,47 +745,6 @@ const EncryptedContent_ContactUpdate_Type$json = {
],
};
-@$core.Deprecated('Use encryptedContentDescriptor instead')
-const EncryptedContent_PushKeys$json = {
- '1': 'PushKeys',
- '2': [
- {
- '1': 'type',
- '3': 1,
- '4': 1,
- '5': 14,
- '6': '.EncryptedContent.PushKeys.Type',
- '10': 'type'
- },
- {'1': 'key_id', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'keyId', '17': true},
- {'1': 'key', '3': 3, '4': 1, '5': 12, '9': 1, '10': 'key', '17': true},
- {
- '1': 'created_at',
- '3': 4,
- '4': 1,
- '5': 3,
- '9': 2,
- '10': 'createdAt',
- '17': true
- },
- ],
- '4': [EncryptedContent_PushKeys_Type$json],
- '8': [
- {'1': '_key_id'},
- {'1': '_key'},
- {'1': '_created_at'},
- ],
-};
-
-@$core.Deprecated('Use encryptedContentDescriptor instead')
-const EncryptedContent_PushKeys_Type$json = {
- '1': 'Type',
- '2': [
- {'1': 'REQUEST', '2': 0},
- {'1': 'UPDATE', '2': 1},
- ],
-};
-
@$core.Deprecated('Use encryptedContentDescriptor instead')
const EncryptedContent_FlameSync$json = {
'1': 'FlameSync',
@@ -891,107 +838,102 @@ final $typed_data.Uint8List encryptedContentDescriptor = $convert.base64Decode(
'91cGRhdGUYCCABKAsyHy5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGVICFINY29udGFj'
'dFVwZGF0ZYgBARJOCg9jb250YWN0X3JlcXVlc3QYCSABKAsyIC5FbmNyeXB0ZWRDb250ZW50Lk'
'NvbnRhY3RSZXF1ZXN0SAlSDmNvbnRhY3RSZXF1ZXN0iAEBEj8KCmZsYW1lX3N5bmMYCiABKAsy'
- 'Gy5FbmNyeXB0ZWRDb250ZW50LkZsYW1lU3luY0gKUglmbGFtZVN5bmOIAQESPAoJcHVzaF9rZX'
- 'lzGAsgASgLMhouRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5c0gLUghwdXNoS2V5c4gBARI7Cghy'
- 'ZWFjdGlvbhgMIAEoCzIaLkVuY3J5cHRlZENvbnRlbnQuUmVhY3Rpb25IDFIIcmVhY3Rpb26IAQ'
- 'ESRQoMdGV4dF9tZXNzYWdlGA0gASgLMh0uRW5jcnlwdGVkQ29udGVudC5UZXh0TWVzc2FnZUgN'
- 'Ugt0ZXh0TWVzc2FnZYgBARJFCgxncm91cF9jcmVhdGUYDiABKAsyHS5FbmNyeXB0ZWRDb250ZW'
- '50Lkdyb3VwQ3JlYXRlSA5SC2dyb3VwQ3JlYXRliAEBEj8KCmdyb3VwX2pvaW4YDyABKAsyGy5F'
- 'bmNyeXB0ZWRDb250ZW50Lkdyb3VwSm9pbkgPUglncm91cEpvaW6IAQESRQoMZ3JvdXBfdXBkYX'
- 'RlGBAgASgLMh0uRW5jcnlwdGVkQ29udGVudC5Hcm91cFVwZGF0ZUgQUgtncm91cFVwZGF0ZYgB'
- 'ARJiChdyZXNlbmRfZ3JvdXBfcHVibGljX2tleRgRIAEoCzImLkVuY3J5cHRlZENvbnRlbnQuUm'
- 'VzZW5kR3JvdXBQdWJsaWNLZXlIEVIUcmVzZW5kR3JvdXBQdWJsaWNLZXmIAQESSwoOZXJyb3Jf'
- 'bWVzc2FnZXMYEiABKAsyHy5FbmNyeXB0ZWRDb250ZW50LkVycm9yTWVzc2FnZXNIElINZXJyb3'
- 'JNZXNzYWdlc4gBARJkChdhZGRpdGlvbmFsX2RhdGFfbWVzc2FnZRgTIAEoCzInLkVuY3J5cHRl'
- 'ZENvbnRlbnQuQWRkaXRpb25hbERhdGFNZXNzYWdlSBNSFWFkZGl0aW9uYWxEYXRhTWVzc2FnZY'
- 'gBARJRChB0eXBpbmdfaW5kaWNhdG9yGBQgASgLMiEuRW5jcnlwdGVkQ29udGVudC5UeXBpbmdJ'
- 'bmRpY2F0b3JIFFIPdHlwaW5nSW5kaWNhdG9yiAEBEmEKFnVzZXJfZGlzY292ZXJ5X3JlcXVlc3'
- 'QYFiABKAsyJi5FbmNyeXB0ZWRDb250ZW50LlVzZXJEaXNjb3ZlcnlSZXF1ZXN0SBVSFHVzZXJE'
- 'aXNjb3ZlcnlSZXF1ZXN0iAEBEl4KFXVzZXJfZGlzY292ZXJ5X3VwZGF0ZRgXIAEoCzIlLkVuY3'
- 'J5cHRlZENvbnRlbnQuVXNlckRpc2NvdmVyeVVwZGF0ZUgWUhN1c2VyRGlzY292ZXJ5VXBkYXRl'
- 'iAEBEmEKFmtleV92ZXJpZmljYXRpb25fcHJvb2YYGCABKAsyJi5FbmNyeXB0ZWRDb250ZW50Lk'
- 'tleVZlcmlmaWNhdGlvblByb29mSBdSFGtleVZlcmlmaWNhdGlvblByb29miAEBEmAKFXBhc3N3'
- 'b3JkbGVzc19yZWNvdmVyeRgaIAEoCzImLkVuY3J5cHRlZENvbnRlbnQuUGFzc3dvcmRMZXNzUm'
- 'Vjb3ZlcnlIGFIUcGFzc3dvcmRsZXNzUmVjb3ZlcnmIAQESfAofcGFzc3dvcmRsZXNzX3JlY292'
- 'ZXJ5X2hlYXJ0YmVhdBgbIAEoCzIvLkVuY3J5cHRlZENvbnRlbnQuUGFzc3dvcmRMZXNzUmVjb3'
- 'ZlcnlIZWFydGJlYXRIGVIdcGFzc3dvcmRsZXNzUmVjb3ZlcnlIZWFydGJlYXSIAQEalgIKDUVy'
- 'cm9yTWVzc2FnZXMSOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuRXJyb3JNZXNzYW'
- 'dlcy5UeXBlUgR0eXBlEiwKEnJlbGF0ZWRfcmVjZWlwdF9pZBgCIAEoCVIQcmVsYXRlZFJlY2Vp'
- 'cHRJZCKcAQoEVHlwZRI8CjhFUlJPUl9QUk9DRVNTSU5HX01FU1NBR0VfQ1JFQVRFRF9BQ0NPVU'
- '5UX1JFUVVFU1RfSU5TVEVBRBAAEhgKFFVOS05PV05fTUVTU0FHRV9UWVBFEAISFwoTU0VTU0lP'
- 'Tl9PVVRfT0ZfU1lOQxADEiMKH0dST1VQX05PVF9GT1VORF9PUl9OT1RfQV9NRU1CRVIQBBqHAQ'
- 'oLR3JvdXBDcmVhdGUSGwoJc3RhdGVfa2V5GAMgASgMUghzdGF0ZUtleRIoChBncm91cF9wdWJs'
- 'aWNfa2V5GAQgASgMUg5ncm91cFB1YmxpY0tleRIiCgpncm91cF9uYW1lGAUgASgJSABSCWdyb3'
- 'VwTmFtZYgBAUINCgtfZ3JvdXBfbmFtZRo1CglHcm91cEpvaW4SKAoQZ3JvdXBfcHVibGljX2tl'
- 'eRgBIAEoDFIOZ3JvdXBQdWJsaWNLZXkaFgoUUmVzZW5kR3JvdXBQdWJsaWNLZXkayAIKC0dyb3'
- 'VwVXBkYXRlEioKEWdyb3VwX2FjdGlvbl90eXBlGAEgASgJUg9ncm91cEFjdGlvblR5cGUSMwoT'
- 'YWZmZWN0ZWRfY29udGFjdF9pZBgCIAEoA0gAUhFhZmZlY3RlZENvbnRhY3RJZIgBARIpCg5uZX'
- 'dfZ3JvdXBfbmFtZRgDIAEoCUgBUgxuZXdHcm91cE5hbWWIAQESVwombmV3X2RlbGV0ZV9tZXNz'
- 'YWdlc19hZnRlcl9taWxsaXNlY29uZHMYBCABKANIAlIibmV3RGVsZXRlTWVzc2FnZXNBZnRlck'
- '1pbGxpc2Vjb25kc4gBAUIWChRfYWZmZWN0ZWRfY29udGFjdF9pZEIRCg9fbmV3X2dyb3VwX25h'
- 'bWVCKQonX25ld19kZWxldGVfbWVzc2FnZXNfYWZ0ZXJfbWlsbGlzZWNvbmRzGq8BCgtUZXh0TW'
- 'Vzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2VuZGVyTWVzc2FnZUlkEhIKBHRl'
- 'eHQYAiABKAlSBHRleHQSHAoJdGltZXN0YW1wGAMgASgDUgl0aW1lc3RhbXASLQoQcXVvdGVfbW'
- 'Vzc2FnZV9pZBgEIAEoCUgAUg5xdW90ZU1lc3NhZ2VJZIgBAUITChFfcXVvdGVfbWVzc2FnZV9p'
- 'ZBrOAQoVQWRkaXRpb25hbERhdGFNZXNzYWdlEioKEXNlbmRlcl9tZXNzYWdlX2lkGAEgASgJUg'
- '9zZW5kZXJNZXNzYWdlSWQSHAoJdGltZXN0YW1wGAIgASgDUgl0aW1lc3RhbXASEgoEdHlwZRgD'
- 'IAEoCVIEdHlwZRI7ChdhZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRgEIAEoDEgAUhVhZGRpdGlvbm'
- 'FsTWVzc2FnZURhdGGIAQFCGgoYX2FkZGl0aW9uYWxfbWVzc2FnZV9kYXRhGmQKCFJlYWN0aW9u'
- 'EioKEXRhcmdldF9tZXNzYWdlX2lkGAEgASgJUg90YXJnZXRNZXNzYWdlSWQSFAoFZW1vamkYAi'
- 'ABKAlSBWVtb2ppEhYKBnJlbW92ZRgDIAEoCFIGcmVtb3ZlGr4CCg1NZXNzYWdlVXBkYXRlEjgK'
- 'BHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50Lk1lc3NhZ2VVcGRhdGUuVHlwZVIEdHlwZR'
- 'IvChFzZW5kZXJfbWVzc2FnZV9pZBgCIAEoCUgAUg9zZW5kZXJNZXNzYWdlSWSIAQESPQobbXVs'
- 'dGlwbGVfdGFyZ2V0X21lc3NhZ2VfaWRzGAMgAygJUhhtdWx0aXBsZVRhcmdldE1lc3NhZ2VJZH'
- 'MSFwoEdGV4dBgEIAEoCUgBUgR0ZXh0iAEBEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW1w'
- 'Ii0KBFR5cGUSCgoGREVMRVRFEAASDQoJRURJVF9URVhUEAESCgoGT1BFTkVEEAJCFAoSX3Nlbm'
- 'Rlcl9tZXNzYWdlX2lkQgcKBV90ZXh0GoUGCgVNZWRpYRIqChFzZW5kZXJfbWVzc2FnZV9pZBgB'
- 'IAEoCVIPc2VuZGVyTWVzc2FnZUlkEjAKBHR5cGUYAiABKA4yHC5FbmNyeXB0ZWRDb250ZW50Lk'
- '1lZGlhLlR5cGVSBHR5cGUSRgodZGlzcGxheV9saW1pdF9pbl9taWxsaXNlY29uZHMYAyABKANI'
- 'AFIaZGlzcGxheUxpbWl0SW5NaWxsaXNlY29uZHOIAQESNwoXcmVxdWlyZXNfYXV0aGVudGljYX'
- 'Rpb24YBCABKAhSFnJlcXVpcmVzQXV0aGVudGljYXRpb24SHAoJdGltZXN0YW1wGAUgASgDUgl0'
- 'aW1lc3RhbXASLQoQcXVvdGVfbWVzc2FnZV9pZBgGIAEoCUgBUg5xdW90ZU1lc3NhZ2VJZIgBAR'
- 'IqCg5kb3dubG9hZF90b2tlbhgHIAEoDEgCUg1kb3dubG9hZFRva2VuiAEBEioKDmVuY3J5cHRp'
- 'b25fa2V5GAggASgMSANSDWVuY3J5cHRpb25LZXmIAQESKgoOZW5jcnlwdGlvbl9tYWMYCSABKA'
- 'xIBFINZW5jcnlwdGlvbk1hY4gBARIuChBlbmNyeXB0aW9uX25vbmNlGAogASgMSAVSD2VuY3J5'
- 'cHRpb25Ob25jZYgBARI7ChdhZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRgLIAEoDEgGUhVhZGRpdG'
- 'lvbmFsTWVzc2FnZURhdGGIAQEiPgoEVHlwZRIMCghSRVVQTE9BRBAAEgkKBUlNQUdFEAESCQoF'
- 'VklERU8QAhIHCgNHSUYQAxIJCgVBVURJTxAEQiAKHl9kaXNwbGF5X2xpbWl0X2luX21pbGxpc2'
- 'Vjb25kc0ITChFfcXVvdGVfbWVzc2FnZV9pZEIRCg9fZG93bmxvYWRfdG9rZW5CEQoPX2VuY3J5'
- 'cHRpb25fa2V5QhEKD19lbmNyeXB0aW9uX21hY0ITChFfZW5jcnlwdGlvbl9ub25jZUIaChhfYW'
- 'RkaXRpb25hbF9tZXNzYWdlX2RhdGEaqQEKC01lZGlhVXBkYXRlEjYKBHR5cGUYASABKA4yIi5F'
- 'bmNyeXB0ZWRDb250ZW50Lk1lZGlhVXBkYXRlLlR5cGVSBHR5cGUSKgoRdGFyZ2V0X21lc3NhZ2'
- 'VfaWQYAiABKAlSD3RhcmdldE1lc3NhZ2VJZCI2CgRUeXBlEgwKCFJFT1BFTkVEEAASCgoGU1RP'
- 'UkVEEAESFAoQREVDUllQVElPTl9FUlJPUhACGngKDkNvbnRhY3RSZXF1ZXN0EjkKBHR5cGUYAS'
- 'ABKA4yJS5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RSZXF1ZXN0LlR5cGVSBHR5cGUiKwoEVHlw'
- 'ZRILCgdSRVFVRVNUEAASCgoGUkVKRUNUEAESCgoGQUNDRVBUEAIapAIKDUNvbnRhY3RVcGRhdG'
- 'USOAoEdHlwZRgBIAEoDjIkLkVuY3J5cHRlZENvbnRlbnQuQ29udGFjdFVwZGF0ZS5UeXBlUgR0'
- 'eXBlEjcKFWF2YXRhcl9zdmdfY29tcHJlc3NlZBgCIAEoDEgAUhNhdmF0YXJTdmdDb21wcmVzc2'
- 'VkiAEBEh8KCHVzZXJuYW1lGAMgASgJSAFSCHVzZXJuYW1liAEBEiYKDGRpc3BsYXlfbmFtZRgE'
- 'IAEoCUgCUgtkaXNwbGF5TmFtZYgBASIfCgRUeXBlEgsKB1JFUVVFU1QQABIKCgZVUERBVEUQAU'
- 'IYChZfYXZhdGFyX3N2Z19jb21wcmVzc2VkQgsKCV91c2VybmFtZUIPCg1fZGlzcGxheV9uYW1l'
- 'GtkBCghQdXNoS2V5cxIzCgR0eXBlGAEgASgOMh8uRW5jcnlwdGVkQ29udGVudC5QdXNoS2V5cy'
- '5UeXBlUgR0eXBlEhoKBmtleV9pZBgCIAEoA0gAUgVrZXlJZIgBARIVCgNrZXkYAyABKAxIAVID'
- 'a2V5iAEBEiIKCmNyZWF0ZWRfYXQYBCABKANIAlIJY3JlYXRlZEF0iAEBIh8KBFR5cGUSCwoHUk'
- 'VRVUVTVBAAEgoKBlVQREFURRABQgkKB19rZXlfaWRCBgoEX2tleUINCgtfY3JlYXRlZF9hdBqv'
- 'AQoJRmxhbWVTeW5jEiMKDWZsYW1lX2NvdW50ZXIYASABKANSDGZsYW1lQ291bnRlchI5ChlsYX'
- 'N0X2ZsYW1lX2NvdW50ZXJfY2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlEh8K'
- 'C2Jlc3RfZnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEiEKDGZvcmNlX3VwZGF0ZRgEIAEoCFILZm'
- '9yY2VVcGRhdGUaTQoPVHlwaW5nSW5kaWNhdG9yEhsKCWlzX3R5cGluZxgBIAEoCFIIaXNUeXBp'
- 'bmcSHQoKY3JlYXRlZF9hdBgCIAEoA1IJY3JlYXRlZEF0Gj8KFFVzZXJEaXNjb3ZlcnlSZXF1ZX'
- 'N0EicKD2N1cnJlbnRfdmVyc2lvbhgBIAEoDFIOY3VycmVudFZlcnNpb24aMQoTVXNlckRpc2Nv'
- 'dmVyeVVwZGF0ZRIaCghtZXNzYWdlcxgBIAMoDFIIbWVzc2FnZXMaPQoUS2V5VmVyaWZpY2F0aW'
- '9uUHJvb2YSJQoOY2FsY3VsYXRlZF9tYWMYASABKAxSDWNhbGN1bGF0ZWRNYWMamwEKFFBhc3N3'
- 'b3JkTGVzc1JlY292ZXJ5EjUKE3JlY292ZXJ5U2VjcmV0U2hhcmUYASABKAxIAFITcmVjb3Zlcn'
- 'lTZWNyZXRTaGFyZYgBARIWCgZkZWxldGUYAiABKAhSBmRlbGV0ZRIcCgl0aHJlc2hvbGQYAyAB'
- 'KANSCXRocmVzaG9sZEIWChRfcmVjb3ZlcnlTZWNyZXRTaGFyZRozCh1QYXNzd29yZExlc3NSZW'
- 'NvdmVyeUhlYXJ0YmVhdBISCgRoYXNoGAEgASgMUgRoYXNoQgsKCV9ncm91cF9pZEIRCg9faXNf'
- 'ZGlyZWN0X2NoYXRCGQoXX3NlbmRlcl9wcm9maWxlX2NvdW50ZXJCIAoeX3NlbmRlcl91c2VyX2'
- 'Rpc2NvdmVyeV92ZXJzaW9uQhwKGl9hc2tfZm9yX2ZyaWVuZF9wcm9tb3Rpb25zQhEKD19tZXNz'
- 'YWdlX3VwZGF0ZUIICgZfbWVkaWFCDwoNX21lZGlhX3VwZGF0ZUIRCg9fY29udGFjdF91cGRhdG'
- 'VCEgoQX2NvbnRhY3RfcmVxdWVzdEINCgtfZmxhbWVfc3luY0IMCgpfcHVzaF9rZXlzQgsKCV9y'
- 'ZWFjdGlvbkIPCg1fdGV4dF9tZXNzYWdlQg8KDV9ncm91cF9jcmVhdGVCDQoLX2dyb3VwX2pvaW'
- '5CDwoNX2dyb3VwX3VwZGF0ZUIaChhfcmVzZW5kX2dyb3VwX3B1YmxpY19rZXlCEQoPX2Vycm9y'
- 'X21lc3NhZ2VzQhoKGF9hZGRpdGlvbmFsX2RhdGFfbWVzc2FnZUITChFfdHlwaW5nX2luZGljYX'
- 'RvckIZChdfdXNlcl9kaXNjb3ZlcnlfcmVxdWVzdEIYChZfdXNlcl9kaXNjb3ZlcnlfdXBkYXRl'
- 'QhkKF19rZXlfdmVyaWZpY2F0aW9uX3Byb29mQhgKFl9wYXNzd29yZGxlc3NfcmVjb3ZlcnlCIg'
- 'ogX3Bhc3N3b3JkbGVzc19yZWNvdmVyeV9oZWFydGJlYXQ=');
+ 'Gy5FbmNyeXB0ZWRDb250ZW50LkZsYW1lU3luY0gKUglmbGFtZVN5bmOIAQESOwoIcmVhY3Rpb2'
+ '4YDCABKAsyGi5FbmNyeXB0ZWRDb250ZW50LlJlYWN0aW9uSAtSCHJlYWN0aW9uiAEBEkUKDHRl'
+ 'eHRfbWVzc2FnZRgNIAEoCzIdLkVuY3J5cHRlZENvbnRlbnQuVGV4dE1lc3NhZ2VIDFILdGV4dE'
+ '1lc3NhZ2WIAQESRQoMZ3JvdXBfY3JlYXRlGA4gASgLMh0uRW5jcnlwdGVkQ29udGVudC5Hcm91'
+ 'cENyZWF0ZUgNUgtncm91cENyZWF0ZYgBARI/Cgpncm91cF9qb2luGA8gASgLMhsuRW5jcnlwdG'
+ 'VkQ29udGVudC5Hcm91cEpvaW5IDlIJZ3JvdXBKb2luiAEBEkUKDGdyb3VwX3VwZGF0ZRgQIAEo'
+ 'CzIdLkVuY3J5cHRlZENvbnRlbnQuR3JvdXBVcGRhdGVID1ILZ3JvdXBVcGRhdGWIAQESYgoXcm'
+ 'VzZW5kX2dyb3VwX3B1YmxpY19rZXkYESABKAsyJi5FbmNyeXB0ZWRDb250ZW50LlJlc2VuZEdy'
+ 'b3VwUHVibGljS2V5SBBSFHJlc2VuZEdyb3VwUHVibGljS2V5iAEBEksKDmVycm9yX21lc3NhZ2'
+ 'VzGBIgASgLMh8uRW5jcnlwdGVkQ29udGVudC5FcnJvck1lc3NhZ2VzSBFSDWVycm9yTWVzc2Fn'
+ 'ZXOIAQESZAoXYWRkaXRpb25hbF9kYXRhX21lc3NhZ2UYEyABKAsyJy5FbmNyeXB0ZWRDb250ZW'
+ '50LkFkZGl0aW9uYWxEYXRhTWVzc2FnZUgSUhVhZGRpdGlvbmFsRGF0YU1lc3NhZ2WIAQESUQoQ'
+ 'dHlwaW5nX2luZGljYXRvchgUIAEoCzIhLkVuY3J5cHRlZENvbnRlbnQuVHlwaW5nSW5kaWNhdG'
+ '9ySBNSD3R5cGluZ0luZGljYXRvcogBARJhChZ1c2VyX2Rpc2NvdmVyeV9yZXF1ZXN0GBYgASgL'
+ 'MiYuRW5jcnlwdGVkQ29udGVudC5Vc2VyRGlzY292ZXJ5UmVxdWVzdEgUUhR1c2VyRGlzY292ZX'
+ 'J5UmVxdWVzdIgBARJeChV1c2VyX2Rpc2NvdmVyeV91cGRhdGUYFyABKAsyJS5FbmNyeXB0ZWRD'
+ 'b250ZW50LlVzZXJEaXNjb3ZlcnlVcGRhdGVIFVITdXNlckRpc2NvdmVyeVVwZGF0ZYgBARJhCh'
+ 'ZrZXlfdmVyaWZpY2F0aW9uX3Byb29mGBggASgLMiYuRW5jcnlwdGVkQ29udGVudC5LZXlWZXJp'
+ 'ZmljYXRpb25Qcm9vZkgWUhRrZXlWZXJpZmljYXRpb25Qcm9vZogBARJgChVwYXNzd29yZGxlc3'
+ 'NfcmVjb3ZlcnkYGiABKAsyJi5FbmNyeXB0ZWRDb250ZW50LlBhc3N3b3JkTGVzc1JlY292ZXJ5'
+ 'SBdSFHBhc3N3b3JkbGVzc1JlY292ZXJ5iAEBEnwKH3Bhc3N3b3JkbGVzc19yZWNvdmVyeV9oZW'
+ 'FydGJlYXQYGyABKAsyLy5FbmNyeXB0ZWRDb250ZW50LlBhc3N3b3JkTGVzc1JlY292ZXJ5SGVh'
+ 'cnRiZWF0SBhSHXBhc3N3b3JkbGVzc1JlY292ZXJ5SGVhcnRiZWF0iAEBGpYCCg1FcnJvck1lc3'
+ 'NhZ2VzEjgKBHR5cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkVycm9yTWVzc2FnZXMuVHlw'
+ 'ZVIEdHlwZRIsChJyZWxhdGVkX3JlY2VpcHRfaWQYAiABKAlSEHJlbGF0ZWRSZWNlaXB0SWQinA'
+ 'EKBFR5cGUSPAo4RVJST1JfUFJPQ0VTU0lOR19NRVNTQUdFX0NSRUFURURfQUNDT1VOVF9SRVFV'
+ 'RVNUX0lOU1RFQUQQABIYChRVTktOT1dOX01FU1NBR0VfVFlQRRACEhcKE1NFU1NJT05fT1VUX0'
+ '9GX1NZTkMQAxIjCh9HUk9VUF9OT1RfRk9VTkRfT1JfTk9UX0FfTUVNQkVSEAQahwEKC0dyb3Vw'
+ 'Q3JlYXRlEhsKCXN0YXRlX2tleRgDIAEoDFIIc3RhdGVLZXkSKAoQZ3JvdXBfcHVibGljX2tleR'
+ 'gEIAEoDFIOZ3JvdXBQdWJsaWNLZXkSIgoKZ3JvdXBfbmFtZRgFIAEoCUgAUglncm91cE5hbWWI'
+ 'AQFCDQoLX2dyb3VwX25hbWUaNQoJR3JvdXBKb2luEigKEGdyb3VwX3B1YmxpY19rZXkYASABKA'
+ 'xSDmdyb3VwUHVibGljS2V5GhYKFFJlc2VuZEdyb3VwUHVibGljS2V5GsgCCgtHcm91cFVwZGF0'
+ 'ZRIqChFncm91cF9hY3Rpb25fdHlwZRgBIAEoCVIPZ3JvdXBBY3Rpb25UeXBlEjMKE2FmZmVjdG'
+ 'VkX2NvbnRhY3RfaWQYAiABKANIAFIRYWZmZWN0ZWRDb250YWN0SWSIAQESKQoObmV3X2dyb3Vw'
+ 'X25hbWUYAyABKAlIAVIMbmV3R3JvdXBOYW1liAEBElcKJm5ld19kZWxldGVfbWVzc2FnZXNfYW'
+ 'Z0ZXJfbWlsbGlzZWNvbmRzGAQgASgDSAJSIm5ld0RlbGV0ZU1lc3NhZ2VzQWZ0ZXJNaWxsaXNl'
+ 'Y29uZHOIAQFCFgoUX2FmZmVjdGVkX2NvbnRhY3RfaWRCEQoPX25ld19ncm91cF9uYW1lQikKJ1'
+ '9uZXdfZGVsZXRlX21lc3NhZ2VzX2FmdGVyX21pbGxpc2Vjb25kcxqvAQoLVGV4dE1lc3NhZ2US'
+ 'KgoRc2VuZGVyX21lc3NhZ2VfaWQYASABKAlSD3NlbmRlck1lc3NhZ2VJZBISCgR0ZXh0GAIgAS'
+ 'gJUgR0ZXh0EhwKCXRpbWVzdGFtcBgDIAEoA1IJdGltZXN0YW1wEi0KEHF1b3RlX21lc3NhZ2Vf'
+ 'aWQYBCABKAlIAFIOcXVvdGVNZXNzYWdlSWSIAQFCEwoRX3F1b3RlX21lc3NhZ2VfaWQazgEKFU'
+ 'FkZGl0aW9uYWxEYXRhTWVzc2FnZRIqChFzZW5kZXJfbWVzc2FnZV9pZBgBIAEoCVIPc2VuZGVy'
+ 'TWVzc2FnZUlkEhwKCXRpbWVzdGFtcBgCIAEoA1IJdGltZXN0YW1wEhIKBHR5cGUYAyABKAlSBH'
+ 'R5cGUSOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYBCABKAxIAFIVYWRkaXRpb25hbE1lc3Nh'
+ 'Z2VEYXRhiAEBQhoKGF9hZGRpdGlvbmFsX21lc3NhZ2VfZGF0YRpkCghSZWFjdGlvbhIqChF0YX'
+ 'JnZXRfbWVzc2FnZV9pZBgBIAEoCVIPdGFyZ2V0TWVzc2FnZUlkEhQKBWVtb2ppGAIgASgJUgVl'
+ 'bW9qaRIWCgZyZW1vdmUYAyABKAhSBnJlbW92ZRq+AgoNTWVzc2FnZVVwZGF0ZRI4CgR0eXBlGA'
+ 'EgASgOMiQuRW5jcnlwdGVkQ29udGVudC5NZXNzYWdlVXBkYXRlLlR5cGVSBHR5cGUSLwoRc2Vu'
+ 'ZGVyX21lc3NhZ2VfaWQYAiABKAlIAFIPc2VuZGVyTWVzc2FnZUlkiAEBEj0KG211bHRpcGxlX3'
+ 'RhcmdldF9tZXNzYWdlX2lkcxgDIAMoCVIYbXVsdGlwbGVUYXJnZXRNZXNzYWdlSWRzEhcKBHRl'
+ 'eHQYBCABKAlIAVIEdGV4dIgBARIcCgl0aW1lc3RhbXAYBSABKANSCXRpbWVzdGFtcCItCgRUeX'
+ 'BlEgoKBkRFTEVURRAAEg0KCUVESVRfVEVYVBABEgoKBk9QRU5FRBACQhQKEl9zZW5kZXJfbWVz'
+ 'c2FnZV9pZEIHCgVfdGV4dBqFBgoFTWVkaWESKgoRc2VuZGVyX21lc3NhZ2VfaWQYASABKAlSD3'
+ 'NlbmRlck1lc3NhZ2VJZBIwCgR0eXBlGAIgASgOMhwuRW5jcnlwdGVkQ29udGVudC5NZWRpYS5U'
+ 'eXBlUgR0eXBlEkYKHWRpc3BsYXlfbGltaXRfaW5fbWlsbGlzZWNvbmRzGAMgASgDSABSGmRpc3'
+ 'BsYXlMaW1pdEluTWlsbGlzZWNvbmRziAEBEjcKF3JlcXVpcmVzX2F1dGhlbnRpY2F0aW9uGAQg'
+ 'ASgIUhZyZXF1aXJlc0F1dGhlbnRpY2F0aW9uEhwKCXRpbWVzdGFtcBgFIAEoA1IJdGltZXN0YW'
+ '1wEi0KEHF1b3RlX21lc3NhZ2VfaWQYBiABKAlIAVIOcXVvdGVNZXNzYWdlSWSIAQESKgoOZG93'
+ 'bmxvYWRfdG9rZW4YByABKAxIAlINZG93bmxvYWRUb2tlbogBARIqCg5lbmNyeXB0aW9uX2tleR'
+ 'gIIAEoDEgDUg1lbmNyeXB0aW9uS2V5iAEBEioKDmVuY3J5cHRpb25fbWFjGAkgASgMSARSDWVu'
+ 'Y3J5cHRpb25NYWOIAQESLgoQZW5jcnlwdGlvbl9ub25jZRgKIAEoDEgFUg9lbmNyeXB0aW9uTm'
+ '9uY2WIAQESOwoXYWRkaXRpb25hbF9tZXNzYWdlX2RhdGEYCyABKAxIBlIVYWRkaXRpb25hbE1l'
+ 'c3NhZ2VEYXRhiAEBIj4KBFR5cGUSDAoIUkVVUExPQUQQABIJCgVJTUFHRRABEgkKBVZJREVPEA'
+ 'ISBwoDR0lGEAMSCQoFQVVESU8QBEIgCh5fZGlzcGxheV9saW1pdF9pbl9taWxsaXNlY29uZHNC'
+ 'EwoRX3F1b3RlX21lc3NhZ2VfaWRCEQoPX2Rvd25sb2FkX3Rva2VuQhEKD19lbmNyeXB0aW9uX2'
+ 'tleUIRCg9fZW5jcnlwdGlvbl9tYWNCEwoRX2VuY3J5cHRpb25fbm9uY2VCGgoYX2FkZGl0aW9u'
+ 'YWxfbWVzc2FnZV9kYXRhGqkBCgtNZWRpYVVwZGF0ZRI2CgR0eXBlGAEgASgOMiIuRW5jcnlwdG'
+ 'VkQ29udGVudC5NZWRpYVVwZGF0ZS5UeXBlUgR0eXBlEioKEXRhcmdldF9tZXNzYWdlX2lkGAIg'
+ 'ASgJUg90YXJnZXRNZXNzYWdlSWQiNgoEVHlwZRIMCghSRU9QRU5FRBAAEgoKBlNUT1JFRBABEh'
+ 'QKEERFQ1JZUFRJT05fRVJST1IQAhp4Cg5Db250YWN0UmVxdWVzdBI5CgR0eXBlGAEgASgOMiUu'
+ 'RW5jcnlwdGVkQ29udGVudC5Db250YWN0UmVxdWVzdC5UeXBlUgR0eXBlIisKBFR5cGUSCwoHUk'
+ 'VRVUVTVBAAEgoKBlJFSkVDVBABEgoKBkFDQ0VQVBACGqQCCg1Db250YWN0VXBkYXRlEjgKBHR5'
+ 'cGUYASABKA4yJC5FbmNyeXB0ZWRDb250ZW50LkNvbnRhY3RVcGRhdGUuVHlwZVIEdHlwZRI3Ch'
+ 'VhdmF0YXJfc3ZnX2NvbXByZXNzZWQYAiABKAxIAFITYXZhdGFyU3ZnQ29tcHJlc3NlZIgBARIf'
+ 'Cgh1c2VybmFtZRgDIAEoCUgBUgh1c2VybmFtZYgBARImCgxkaXNwbGF5X25hbWUYBCABKAlIAl'
+ 'ILZGlzcGxheU5hbWWIAQEiHwoEVHlwZRILCgdSRVFVRVNUEAASCgoGVVBEQVRFEAFCGAoWX2F2'
+ 'YXRhcl9zdmdfY29tcHJlc3NlZEILCglfdXNlcm5hbWVCDwoNX2Rpc3BsYXlfbmFtZRqvAQoJRm'
+ 'xhbWVTeW5jEiMKDWZsYW1lX2NvdW50ZXIYASABKANSDGZsYW1lQ291bnRlchI5ChlsYXN0X2Zs'
+ 'YW1lX2NvdW50ZXJfY2hhbmdlGAIgASgDUhZsYXN0RmxhbWVDb3VudGVyQ2hhbmdlEh8KC2Jlc3'
+ 'RfZnJpZW5kGAMgASgIUgpiZXN0RnJpZW5kEiEKDGZvcmNlX3VwZGF0ZRgEIAEoCFILZm9yY2VV'
+ 'cGRhdGUaTQoPVHlwaW5nSW5kaWNhdG9yEhsKCWlzX3R5cGluZxgBIAEoCFIIaXNUeXBpbmcSHQ'
+ 'oKY3JlYXRlZF9hdBgCIAEoA1IJY3JlYXRlZEF0Gj8KFFVzZXJEaXNjb3ZlcnlSZXF1ZXN0EicK'
+ 'D2N1cnJlbnRfdmVyc2lvbhgBIAEoDFIOY3VycmVudFZlcnNpb24aMQoTVXNlckRpc2NvdmVyeV'
+ 'VwZGF0ZRIaCghtZXNzYWdlcxgBIAMoDFIIbWVzc2FnZXMaPQoUS2V5VmVyaWZpY2F0aW9uUHJv'
+ 'b2YSJQoOY2FsY3VsYXRlZF9tYWMYASABKAxSDWNhbGN1bGF0ZWRNYWMamwEKFFBhc3N3b3JkTG'
+ 'Vzc1JlY292ZXJ5EjUKE3JlY292ZXJ5U2VjcmV0U2hhcmUYASABKAxIAFITcmVjb3ZlcnlTZWNy'
+ 'ZXRTaGFyZYgBARIWCgZkZWxldGUYAiABKAhSBmRlbGV0ZRIcCgl0aHJlc2hvbGQYAyABKANSCX'
+ 'RocmVzaG9sZEIWChRfcmVjb3ZlcnlTZWNyZXRTaGFyZRozCh1QYXNzd29yZExlc3NSZWNvdmVy'
+ 'eUhlYXJ0YmVhdBISCgRoYXNoGAEgASgMUgRoYXNoQgsKCV9ncm91cF9pZEIRCg9faXNfZGlyZW'
+ 'N0X2NoYXRCGQoXX3NlbmRlcl9wcm9maWxlX2NvdW50ZXJCIAoeX3NlbmRlcl91c2VyX2Rpc2Nv'
+ 'dmVyeV92ZXJzaW9uQhwKGl9hc2tfZm9yX2ZyaWVuZF9wcm9tb3Rpb25zQhEKD19tZXNzYWdlX3'
+ 'VwZGF0ZUIICgZfbWVkaWFCDwoNX21lZGlhX3VwZGF0ZUIRCg9fY29udGFjdF91cGRhdGVCEgoQ'
+ 'X2NvbnRhY3RfcmVxdWVzdEINCgtfZmxhbWVfc3luY0ILCglfcmVhY3Rpb25CDwoNX3RleHRfbW'
+ 'Vzc2FnZUIPCg1fZ3JvdXBfY3JlYXRlQg0KC19ncm91cF9qb2luQg8KDV9ncm91cF91cGRhdGVC'
+ 'GgoYX3Jlc2VuZF9ncm91cF9wdWJsaWNfa2V5QhEKD19lcnJvcl9tZXNzYWdlc0IaChhfYWRkaX'
+ 'Rpb25hbF9kYXRhX21lc3NhZ2VCEwoRX3R5cGluZ19pbmRpY2F0b3JCGQoXX3VzZXJfZGlzY292'
+ 'ZXJ5X3JlcXVlc3RCGAoWX3VzZXJfZGlzY292ZXJ5X3VwZGF0ZUIZChdfa2V5X3ZlcmlmaWNhdG'
+ 'lvbl9wcm9vZkIYChZfcGFzc3dvcmRsZXNzX3JlY292ZXJ5QiIKIF9wYXNzd29yZGxlc3NfcmVj'
+ 'b3ZlcnlfaGVhcnRiZWF0');
diff --git a/lib/src/model/protobuf/client/http_requests.proto b/lib/src/model/protobuf/client/http_requests.proto
index a3ed59ce..ce9ce54d 100644
--- a/lib/src/model/protobuf/client/http_requests.proto
+++ b/lib/src/model/protobuf/client/http_requests.proto
@@ -4,7 +4,8 @@ package http_requests;
message TextMessage {
int64 user_id = 1;
bytes body = 2;
- optional bytes push_data = 3;
+ reserved 3; // was: bytes push_data
+ bool wake_receiver = 4;
}
message UploadRequest {
diff --git a/lib/src/services/api/mediafiles/upload.api.dart b/lib/src/services/api/mediafiles/upload.api.dart
index 14a527ce..bf67019c 100644
--- a/lib/src/services/api/mediafiles/upload.api.dart
+++ b/lib/src/services/api/mediafiles/upload.api.dart
@@ -710,7 +710,10 @@ Future _createUploadRequest(MediaFileService media) async {
final messageOnSuccess = TextMessage()
..body = cipherText
- ..userId = Int64(groupMember.contactId);
+ ..userId = Int64(groupMember.contactId)
+ // A media message is user visible, so the server may send the opaque
+ // FCM wake-up once the upload completes.
+ ..wakeReceiver = true;
messagesOnSuccess.add(messageOnSuccess);
downloadTokens.add(downloadToken);
diff --git a/lib/src/services/notifications/background.notifications.dart b/lib/src/services/notifications/background.notifications.dart
index 8b436504..e70ac7f7 100644
--- a/lib/src/services/notifications/background.notifications.dart
+++ b/lib/src/services/notifications/background.notifications.dart
@@ -1,34 +1,4 @@
-import 'dart:async';
-import 'dart:math';
-
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
-
-Future customLocalPushNotification(String title, String msg) async {
- final androidNotificationDetails = AndroidNotificationDetails(
- '1',
- 'System',
- channelDescription: 'System messages.',
- importance: Importance.high,
- priority: Priority.high,
- styleInformation: BigTextStyleInformation(msg),
- icon: 'ic_launcher_foreground',
- );
-
- const darwinNotificationDetails = DarwinNotificationDetails();
- final notificationDetails = NotificationDetails(
- android: androidNotificationDetails,
- iOS: darwinNotificationDetails,
- );
-
- final id = Random.secure().nextInt(9999);
-
- await flutterLocalNotificationsPlugin.show(
- id,
- title,
- msg,
- notificationDetails,
- );
-}
diff --git a/lib/src/services/notifications/fcm.background.dart b/lib/src/services/notifications/fcm.background.dart
deleted file mode 100644
index 01a39396..00000000
--- a/lib/src/services/notifications/fcm.background.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-import 'dart:async';
-import 'dart:io' show Platform;
-
-import 'package:firebase_messaging/firebase_messaging.dart';
-import 'package:sentry_flutter/sentry_flutter.dart';
-import 'package:twonly/globals.dart';
-import 'package:twonly/src/services/background/callback_dispatcher.background.dart';
-import 'package:twonly/src/services/notifications/fcm.notifications.dart';
-import 'package:twonly/src/services/notifications/setup.notifications.dart';
-import 'package:twonly/src/utils/log.dart';
-
-@pragma('vm:entry-point')
-Future firebaseMessagingBackgroundHandler(RemoteMessage message) async {
- SentryWidgetsFlutterBinding.ensureInitialized();
- await AppEnvironment.init();
- final isInitialized = await initBackgroundExecution();
- await setupPushNotification();
- Log.info('Handling a background message: ${message.messageId}');
- await FcmNotificationService.handleRemoteMessage(message);
-
- if (Platform.isAndroid) {
- if (isInitialized) {
- await backgroundFetch(lastExecutionInSecondsLimit: 3);
- }
- } else {
- // make sure every thing run...
- await Future.delayed(const Duration(milliseconds: 2000));
- }
-}
diff --git a/lib/src/services/notifications/fcm.notifications.dart b/lib/src/services/notifications/fcm.notifications.dart
index 73fd37f1..7e6179ce 100644
--- a/lib/src/services/notifications/fcm.notifications.dart
+++ b/lib/src/services/notifications/fcm.notifications.dart
@@ -5,12 +5,8 @@ import 'package:firebase_app_installations/firebase_app_installations.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
-import 'package:twonly/globals.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/secure_storage.keys.dart';
-import 'package:twonly/src/services/background/callback_dispatcher.background.dart';
-import 'package:twonly/src/services/notifications/background.notifications.dart';
-import 'package:twonly/src/services/notifications/fcm.background.dart';
import 'package:twonly/src/services/user.service.dart';
import 'package:twonly/src/utils/log.dart';
@@ -19,14 +15,14 @@ import '../../../firebase_options.dart';
// see more here: https://firebase.google.com/docs/cloud-messaging/flutter/receive?hl=de
class FcmNotificationService {
+ /// FCM is only an opaque wake-up transport now. Delivery is owned natively by
+ /// the iOS Notification Service Extension and by
+ /// `TwonlyFirebaseMessagingService` on Android, both of which call Rust
+ /// directly, so no Dart isolate or message listener is registered here.
static Future initStartup() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
-
- FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
-
- FirebaseMessaging.onMessage.listen(handleRemoteMessage);
}
static Future initAfterUserLoaded() async {
@@ -143,67 +139,6 @@ class FcmNotificationService {
}
}
- static Future handleRemoteMessage(RemoteMessage message) async {
- Log.info('handleRemoteMessage received message: ${message.messageId}');
- await _updateLastFcmMessageTimestamp();
- if (!Platform.isAndroid) {
- Log.error('Got message in Dart while on iOS');
- }
- if (message.notification != null && AppState.isAppInBackground) {
- Log.error(
- 'Got notification but app is in background, so the SDK already have shown the message.',
- );
- return;
- }
-
- // In scenarios like Android Doze Mode or aggressive background restrictions, the OS may kill
- // or heavily restrict network access, preventing the WebSocket from connecting in time.
- // By parsing the FCM data payload offline, we can instantly display the notification, which also
- // prevents FCM from penalizing/downgrading the app's data message priority for failing to show a notification.
- // This is just a workarround until the new Rust decryption is enrolled fully.
- final pushDataString = message.data['push_data'] as String?;
- if (pushDataString != null) {
- final apiState = await RustApi.connectionState();
- if (apiState == ApiConnectionState.connected ||
- apiState == ApiConnectionState.authenticating ||
- apiState == ApiConnectionState.authenticated) {
- Log.info('Got FCM message, but API is connected...');
- } else {
- Log.info('Trying to connect to the API in the background.');
-
- if (await backgroundFetch()) {
- return;
- }
- }
- }
-
- if (message.notification != null || message.data['title'] != null) {
- final title =
- message.notification?.title ?? message.data['title'] as String? ?? '';
- final body =
- message.notification?.body ?? message.data['body'] as String? ?? '';
- await customLocalPushNotification(title, body);
- }
- }
-
- static Future _updateLastFcmMessageTimestamp() async {
- const storage = FlutterSecureStorage();
- final nowMs = DateTime.now().millisecondsSinceEpoch.toString();
- try {
- await storage.write(
- key: SecureStorageKeys.lastFcmMessageTimestamp,
- value: nowMs,
- iOptions: const IOSOptions(
- groupId: 'CN332ZUGRP.eu.twonly.shared',
- accessibility: KeychainAccessibility.first_unlock,
- ),
- );
- Log.info('Updated last FCM message timestamp to $nowMs');
- } catch (e) {
- Log.error('Could not write last FCM message timestamp: $e');
- }
- }
-
static Future updateLastServerMessageTimestamp() async {
const storage = FlutterSecureStorage();
final nowMs = DateTime.now().millisecondsSinceEpoch.toString();
@@ -229,13 +164,6 @@ class FcmNotificationService {
}
const storage = FlutterSecureStorage();
try {
- final lastFcmStr = await storage.read(
- key: SecureStorageKeys.lastFcmMessageTimestamp,
- iOptions: const IOSOptions(
- groupId: 'CN332ZUGRP.eu.twonly.shared',
- accessibility: KeychainAccessibility.first_unlock,
- ),
- );
final lastServerStr = await storage.read(
key: SecureStorageKeys.lastServerMessageTimestamp,
iOptions: const IOSOptions(
@@ -247,13 +175,12 @@ class FcmNotificationService {
final now = DateTime.now();
final threeDaysAgo = now.subtract(const Duration(days: 3));
- DateTime? lastFcmTime;
- if (lastFcmStr != null) {
- final ms = int.tryParse(lastFcmStr);
- if (ms != null) {
- lastFcmTime = DateTime.fromMillisecondsSinceEpoch(ms);
- }
- }
+ // Recorded by the Rust notification worker, because neither platform
+ // starts Flutter for a background wake-up any more.
+ final lastFcmWakeup = userService.currentUser.lastFcmWakeupAt;
+ final lastFcmTime = lastFcmWakeup == null
+ ? null
+ : DateTime.fromMillisecondsSinceEpoch(lastFcmWakeup * 1000);
if (lastFcmTime != null) {
Log.info(
diff --git a/lib/src/services/notifications/native.notifications.dart b/lib/src/services/notifications/native.notifications.dart
new file mode 100644
index 00000000..80bdb905
--- /dev/null
+++ b/lib/src/services/notifications/native.notifications.dart
@@ -0,0 +1,77 @@
+import 'dart:async';
+import 'dart:io' show Platform;
+
+import 'package:flutter/services.dart';
+import 'package:twonly/src/utils/log.dart';
+
+/// Taps on notifications rendered natively (Android `MessagingStyle`) arrive
+/// through a platform channel instead of the Firebase Messaging plugin, which
+/// no longer owns background delivery.
+///
+/// Only the opaque conversation identifier crosses the channel; the Flutter
+/// route is built here so the native layer stays free of routing knowledge.
+class NativeNotificationService {
+ static const MethodChannel _channel = MethodChannel(
+ 'eu.twonly/notificationTap',
+ );
+
+ static const String _conversationIdKey = 'conversation_id';
+ static const String _notificationIdsKey = 'notification_ids';
+
+ static final StreamController _taps =
+ StreamController.broadcast();
+
+ /// Emits the conversation id of every notification tapped while the app is
+ /// running. A `null` value means the notification had no specific
+ /// conversation and should only open the chats tab.
+ static Stream get taps => _taps.stream;
+
+ static void init() {
+ if (!Platform.isAndroid) return;
+ _channel.setMethodCallHandler((call) async {
+ if (call.method != 'onNotificationTapped') return;
+ _taps.add(_conversationIdOf(call.arguments));
+ });
+ }
+
+ /// Returns the tap that launched the app, or `null` when the app was not
+ /// started from a native notification. The result is consumed once.
+ static Future<({String? conversationId})?> consumeInitialTap() async {
+ if (!Platform.isAndroid) return null;
+ try {
+ final result = await _channel.invokeMapMethod(
+ 'consumeInitialNotification',
+ );
+ if (result == null) return null;
+ return (conversationId: _conversationIdOf(result));
+ } catch (e) {
+ Log.error('Could not read the initial native notification: $e');
+ return null;
+ }
+ }
+
+ /// Withdraws notifications for messages that have just been opened. Native
+ /// notification identifiers are strings on iOS and stable hashes on Android,
+ /// so the conversion stays in the platform implementation.
+ static Future cancelNotifications(
+ Iterable notificationIds,
+ ) async {
+ if (!Platform.isAndroid && !Platform.isIOS) return;
+ final ids = notificationIds.where((id) => id.isNotEmpty).toSet().toList();
+ if (ids.isEmpty) return;
+ try {
+ await _channel.invokeMethod('cancelNotifications', {
+ _notificationIdsKey: ids,
+ });
+ } catch (e) {
+ Log.error('Could not withdraw opened-message notifications: $e');
+ }
+ }
+
+ static String? _conversationIdOf(Object? arguments) {
+ if (arguments is! Map) return null;
+ final conversationId = arguments[_conversationIdKey];
+ if (conversationId is! String || conversationId.isEmpty) return null;
+ return conversationId;
+ }
+}
diff --git a/lib/src/utils/log.dart b/lib/src/utils/log.dart
index 9af0d49e..16e8f267 100644
--- a/lib/src/utils/log.dart
+++ b/lib/src/utils/log.dart
@@ -77,6 +77,17 @@ class Log {
final message = filterLogMessage('$messageInput');
Logger(_getCallerSourceCodeFilename()).fine(message, error, stackTrace);
}
+
+ /// Re-emits a record that was produced outside Dart, keeping the origin's
+ /// level and source location. Deriving either from the Dart call site would
+ /// only ever point back at the forwarding code.
+ static void forward({
+ required Level level,
+ required String source,
+ required Object? messageInput,
+ }) {
+ Logger(source).log(level, filterLogMessage('$messageInput'));
+ }
}
Future loadLogFile() async {
diff --git a/lib/src/visual/views/chats/chat_messages.view.dart b/lib/src/visual/views/chats/chat_messages.view.dart
index 01956147..541d9669 100644
--- a/lib/src/visual/views/chats/chat_messages.view.dart
+++ b/lib/src/visual/views/chats/chat_messages.view.dart
@@ -15,7 +15,7 @@ import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/model/memory_item.model.dart';
import 'package:twonly/src/model/protobuf/client/generated/data.pb.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
-import 'package:twonly/src/services/notifications/background.notifications.dart';
+import 'package:twonly/src/services/notifications/native.notifications.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/components/avatar_icon.comp.dart';
import 'package:twonly/src/visual/components/contact_labels.comp.dart';
@@ -354,10 +354,6 @@ class _ChatMessagesViewState extends State
List groupActions, {
bool reportOpened = false,
}) async {
- if (reportOpened && _isViewActive()) {
- unawaited(flutterLocalNotificationsPlugin.cancelAll());
- }
-
for (final msg in newMessages) {
if (_animationState.hasReceivedFirstBatch &&
!_animationState.knownMessageIds.contains(msg.messageId) &&
@@ -435,12 +431,7 @@ class _ChatMessagesViewState extends State
_animationState.reportedOpenedMessageIds.addAll(
openedMessages[contactId]!,
);
- unawaited(
- RustApi.notifyMessagesOpened(
- contactId: contactId,
- messageIds: openedMessages[contactId]!,
- ),
- );
+ unawaited(_reportMessagesOpened(contactId, openedMessages[contactId]!));
}
}
@@ -472,6 +463,23 @@ class _ChatMessagesViewState extends State
_updateGalleryItems(messages: storedMediaFiles);
}
+ Future _reportMessagesOpened(
+ int contactId,
+ List messageIds,
+ ) async {
+ // Cancel once immediately for an already visible alert, and once after the
+ // durable outbox update to close the small race with a native push worker.
+ await NativeNotificationService.cancelNotifications(messageIds);
+ try {
+ await RustApi.notifyMessagesOpened(
+ contactId: contactId,
+ messageIds: messageIds,
+ );
+ } finally {
+ await NativeNotificationService.cancelNotifications(messageIds);
+ }
+ }
+
void _updateGalleryItems({List? messages, bool force = false}) {
final storedMediaMessages =
messages ??
diff --git a/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart b/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart
index 671708c1..0488cb49 100644
--- a/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart
+++ b/lib/src/visual/views/chats/chat_messages_components/entries/chat_audio_entry.dart
@@ -1,3 +1,5 @@
+import 'dart:async';
+
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
@@ -5,6 +7,7 @@ import 'package:twonly/locator.dart';
import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/database/twonly.db.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
+import 'package:twonly/src/services/notifications/native.notifications.dart';
import 'package:twonly/src/visual/elements/better_text.element.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/common.dart';
import 'package:twonly/src/visual/views/chats/chat_messages_components/entries/friendly_message_time.comp.dart';
@@ -217,10 +220,7 @@ class _InChatAudioPlayerState extends State {
_playerController.startPlayer();
if (widget.message.senderId != null &&
widget.message.openedAt == null) {
- RustApi.notifyMessagesOpened(
- contactId: widget.message.senderId!,
- messageIds: [widget.message.messageId],
- );
+ unawaited(_notifyMessageOpened());
}
}
setState(() {
@@ -259,6 +259,21 @@ class _InChatAudioPlayerState extends State {
],
);
}
+
+ Future _notifyMessageOpened() async {
+ final senderId = widget.message.senderId;
+ if (senderId == null) return;
+ final messageIds = [widget.message.messageId];
+ await NativeNotificationService.cancelNotifications(messageIds);
+ try {
+ await RustApi.notifyMessagesOpened(
+ contactId: senderId,
+ messageIds: messageIds,
+ );
+ } finally {
+ await NativeNotificationService.cancelNotifications(messageIds);
+ }
+ }
}
String formatMsToMinSec(int milliseconds) {
diff --git a/lib/src/visual/views/chats/media_viewer.view.dart b/lib/src/visual/views/chats/media_viewer.view.dart
index 6c63694a..64463681 100644
--- a/lib/src/visual/views/chats/media_viewer.view.dart
+++ b/lib/src/visual/views/chats/media_viewer.view.dart
@@ -7,7 +7,6 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:mutex/mutex.dart';
import 'package:screen_protector/screen_protector.dart';
-import 'package:twonly/globals.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/constants/routes.keys.dart';
import 'package:twonly/src/database/daos/contacts.dao.dart';
@@ -19,7 +18,7 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pb.dart'
import 'package:twonly/src/services/api/mediafiles/download.api.dart';
import 'package:twonly/src/services/api/utils.api.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
-import 'package:twonly/src/services/notifications/background.notifications.dart';
+import 'package:twonly/src/services/notifications/native.notifications.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/helpers/media_view_sizing.helper.dart';
@@ -133,12 +132,6 @@ class _MediaViewerViewState extends State {
final Mutex _messageUpdateLock = Mutex();
- bool _isViewActive() {
- if (!mounted) return false;
- return !AppState.isAppInBackground &&
- (ModalRoute.of(context)?.isCurrent ?? false);
- }
-
Future listenForUnopenedMedia(bool firstRun) async {
_subscription = twonlyDB.messagesDao
.watchMediaNotOpened(widget.group.groupId)
@@ -256,10 +249,6 @@ class _MediaViewerViewState extends State {
showSendTextMessageInput = false;
});
- if (_isViewActive()) {
- unawaited(flutterLocalNotificationsPlugin.cancelAll());
- }
-
final stream = twonlyDB.mediaFilesDao.watchMedia(
allMediaFiles.first.mediaId!,
);
@@ -416,10 +405,15 @@ class _MediaViewerViewState extends State {
markAsOpenMessageIDs = messageIds;
}
- await RustApi.notifyMessagesOpened(
- contactId: currentMessage!.senderId!,
- messageIds: markAsOpenMessageIDs,
- );
+ await NativeNotificationService.cancelNotifications(markAsOpenMessageIDs);
+ try {
+ await RustApi.notifyMessagesOpened(
+ contactId: currentMessage!.senderId!,
+ messageIds: markAsOpenMessageIDs,
+ );
+ } finally {
+ await NativeNotificationService.cancelNotifications(markAsOpenMessageIDs);
+ }
}
Future _setupVideoPlayer(MediaFileService mediaLocal) async {
diff --git a/lib/src/visual/views/home.view.dart b/lib/src/visual/views/home.view.dart
index b86a3c55..e43ac157 100644
--- a/lib/src/visual/views/home.view.dart
+++ b/lib/src/visual/views/home.view.dart
@@ -14,6 +14,7 @@ import 'package:twonly/src/database/tables/mediafiles.table.dart';
import 'package:twonly/src/providers/routing.provider.dart';
import 'package:twonly/src/services/api/mediafiles/upload.api.dart';
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
+import 'package:twonly/src/services/notifications/native.notifications.dart';
import 'package:twonly/src/services/notifications/setup.notifications.dart';
import 'package:twonly/src/utils/log.dart';
import 'package:twonly/src/utils/misc.dart';
@@ -50,6 +51,7 @@ class HomeViewState extends State with WidgetsBindingObserver {
StreamSubscription? _onMessageOpenedAppSub;
StreamSubscription? _homeViewPageIndexSub;
StreamSubscription? _selectNotificationSub;
+ StreamSubscription? _nativeNotificationSub;
StreamSubscription<(String, MediaType)>? _sharedMediaSub;
static Uri? pendingSharedLink;
@@ -113,6 +115,10 @@ class HomeViewState extends State with WidgetsBindingObserver {
streamHomeViewPageIndex.add(0);
});
+ _nativeNotificationSub = NativeNotificationService.taps.listen(
+ _openNativeNotification,
+ );
+
_sharedLinkSub = streamSharedLink.stream.listen((uri) {
HomeViewState.pendingSharedLink = null;
_mainCameraController.setSharedLinkForPreview(uri);
@@ -192,7 +198,20 @@ class HomeViewState extends State with WidgetsBindingObserver {
});
}
+ void _openNativeNotification(String? conversationId) {
+ Log.info('Opened app from a native push notification tap.');
+ if (conversationId != null) {
+ routerProvider.go(Routes.chatsMessages(conversationId));
+ }
+ streamHomeViewPageIndex.add(0);
+ }
+
Future _initAsync() async {
+ final initialNativeTap = await NativeNotificationService.consumeInitialTap();
+ if (initialNativeTap != null) {
+ _openNativeNotification(initialNativeTap.conversationId);
+ }
+
final notificationAppLaunchDetails = await flutterLocalNotificationsPlugin
.getNotificationAppLaunchDetails();
@@ -245,6 +264,7 @@ class HomeViewState extends State with WidgetsBindingObserver {
_onMessageOpenedAppSub?.cancel();
_homeViewPageIndexSub?.cancel();
_selectNotificationSub?.cancel();
+ _nativeNotificationSub?.cancel();
_disableCameraTimer?.cancel();
_mainCameraController.setState = null;
_mainCameraController.closeCamera();
diff --git a/lib/src/visual/views/onboarding/setup.view.dart b/lib/src/visual/views/onboarding/setup.view.dart
index 53efd4b1..162cfff2 100644
--- a/lib/src/visual/views/onboarding/setup.view.dart
+++ b/lib/src/visual/views/onboarding/setup.view.dart
@@ -93,6 +93,7 @@ class SetupView extends StatefulWidget {
class _SetupViewState extends State {
StreamSubscription? _userUpdateStream;
late UserDiscoverySetupState state;
+ bool _setupDone = false;
@override
void initState() {
@@ -102,16 +103,27 @@ class _SetupViewState extends State {
if (widget.onUpdate != null) {
_userUpdateStream = userService.onUserUpdated.listen((u) {
if (userService.currentUser.currentSetupPage == null) {
- widget.onUpdate?.call();
+ _notifySetupDone();
}
});
}
}
+ /// Notifies the parent exactly once. Further user updates must not trigger
+ /// another callback, otherwise a parent popping this view would pop the
+ /// route below it as well.
+ void _notifySetupDone() {
+ if (_setupDone) return;
+ _setupDone = true;
+ unawaited(_userUpdateStream?.cancel());
+ _userUpdateStream = null;
+ widget.onUpdate?.call();
+ }
+
@override
void dispose() {
- super.dispose();
_userUpdateStream?.cancel();
+ super.dispose();
}
@override
@@ -184,7 +196,7 @@ class _SetupViewState extends State {
await UserService.update(
(u) => u.skipSetupPages = true,
);
- widget.onUpdate?.call();
+ _notifySetupDone();
},
variant: MyButtonVariant.text,
child: Text(
diff --git a/lib/src/visual/views/onboarding/setup/components/finish_setup.comp.dart b/lib/src/visual/views/onboarding/setup/components/finish_setup.comp.dart
index 53851dae..8a474021 100644
--- a/lib/src/visual/views/onboarding/setup/components/finish_setup.comp.dart
+++ b/lib/src/visual/views/onboarding/setup/components/finish_setup.comp.dart
@@ -13,13 +13,18 @@ class FinishSetupComp extends StatefulWidget {
class _FinishSetupCompState extends State {
Future onTap() async {
- await context.navPush(
- SetupView(
- onUpdate: () {
- if (mounted) {
- Navigator.pop(context);
- }
- },
+ // Captured before pushing so the callback never resolves the navigator
+ // through this widget's context, which lives below the pushed route.
+ final navigator = Navigator.of(context);
+ await navigator.push(
+ MaterialPageRoute(
+ builder: (_) => SetupView(
+ onUpdate: () {
+ if (mounted && navigator.canPop()) {
+ navigator.pop();
+ }
+ },
+ ),
),
);
}
diff --git a/lib/src/visual/views/settings/developer/informations.view.dart b/lib/src/visual/views/settings/developer/informations.view.dart
index 6faaa7ec..7383df43 100644
--- a/lib/src/visual/views/settings/developer/informations.view.dart
+++ b/lib/src/visual/views/settings/developer/informations.view.dart
@@ -14,7 +14,6 @@ class DeveloperInformationsView extends StatefulWidget {
}
class _DeveloperInformationsViewState extends State {
- String? _lastFcmTimestamp;
String? _lastServerTimestamp;
@override
@@ -26,13 +25,6 @@ class _DeveloperInformationsViewState extends State {
Future _loadInformations({bool showFeedback = false}) async {
const storage = FlutterSecureStorage();
try {
- final lastFcm = await storage.read(
- key: SecureStorageKeys.lastFcmMessageTimestamp,
- iOptions: const IOSOptions(
- groupId: 'CN332ZUGRP.eu.twonly.shared',
- accessibility: KeychainAccessibility.first_unlock,
- ),
- );
final lastServer = await storage.read(
key: SecureStorageKeys.lastServerMessageTimestamp,
iOptions: const IOSOptions(
@@ -42,7 +34,6 @@ class _DeveloperInformationsViewState extends State {
);
if (mounted) {
setState(() {
- _lastFcmTimestamp = lastFcm;
_lastServerTimestamp = lastServer;
});
if (showFeedback) {
@@ -56,6 +47,14 @@ class _DeveloperInformationsViewState extends State {
} catch (_) {}
}
+ String _formatFcmWakeup() {
+ final seconds = userService.currentUser.lastFcmWakeupAt;
+ if (seconds == null) return 'Never';
+ return DateTime.fromMillisecondsSinceEpoch(
+ seconds * 1000,
+ ).toLocal().toString();
+ }
+
String _formatTimestamp(String? timestampStr) {
if (timestampStr == null) return 'Never';
final ms = int.tryParse(timestampStr);
@@ -94,7 +93,7 @@ class _DeveloperInformationsViewState extends State {
const Divider(),
ListTile(
title: const Text('Last FCM Message'),
- subtitle: Text(_formatTimestamp(_lastFcmTimestamp)),
+ subtitle: Text(_formatFcmWakeup()),
),
ListTile(
title: const Text('Last Server Message'),
diff --git a/rust/.sqlx/query-229b83c3c0777a900e71c71d83920a2b93f5df34f29921263f0c960fe0a34ceb.json b/rust/.sqlx/query-0095795ef631e1c4a775c8984bbbe0ca0c546dcc3ef1a608527fa9c42484f763.json
similarity index 81%
rename from rust/.sqlx/query-229b83c3c0777a900e71c71d83920a2b93f5df34f29921263f0c960fe0a34ceb.json
rename to rust/.sqlx/query-0095795ef631e1c4a775c8984bbbe0ca0c546dcc3ef1a608527fa9c42484f763.json
index c3eff5e9..b81aba03 100644
--- a/rust/.sqlx/query-229b83c3c0777a900e71c71d83920a2b93f5df34f29921263f0c960fe0a34ceb.json
+++ b/rust/.sqlx/query-0095795ef631e1c4a775c8984bbbe0ca0c546dcc3ef1a608527fa9c42484f763.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT\n promotion_id,\n public_id,\n from_contact_id,\n threshold,\n announcement_share,\n public_key_verified_timestamp\n FROM user_discovery_other_promotions\n WHERE public_id = ?\n ",
+ "query": "\n SELECT promotion_id, public_id, from_contact_id, threshold,\n announcement_share, public_key_verified_timestamp\n FROM user_discovery_other_promotions\n WHERE public_id = ?\n ",
"describe": {
"columns": [
{
@@ -82,5 +82,5 @@
true
]
},
- "hash": "229b83c3c0777a900e71c71d83920a2b93f5df34f29921263f0c960fe0a34ceb"
+ "hash": "0095795ef631e1c4a775c8984bbbe0ca0c546dcc3ef1a608527fa9c42484f763"
}
diff --git a/rust/.sqlx/query-00e0c32ce73e5448c93de168ac43e7c46db5e3806197aeaf43850d7f8141f63d.json b/rust/.sqlx/query-00e0c32ce73e5448c93de168ac43e7c46db5e3806197aeaf43850d7f8141f63d.json
new file mode 100644
index 00000000..e4f378b9
--- /dev/null
+++ b/rust/.sqlx/query-00e0c32ce73e5448c93de168ac43e7c46db5e3806197aeaf43850d7f8141f63d.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO contacts(user_id, username, accepted) VALUES (?, ?, 1)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "00e0c32ce73e5448c93de168ac43e7c46db5e3806197aeaf43850d7f8141f63d"
+}
diff --git a/rust/.sqlx/query-02f92beaef5af931c37b62b9ebfbdcf0490b17343ba0e11427f572cc9c41b8b4.json b/rust/.sqlx/query-02f92beaef5af931c37b62b9ebfbdcf0490b17343ba0e11427f572cc9c41b8b4.json
new file mode 100644
index 00000000..16557e36
--- /dev/null
+++ b/rust/.sqlx/query-02f92beaef5af931c37b62b9ebfbdcf0490b17343ba0e11427f572cc9c41b8b4.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE messages SET opened_at = ?, opened_by_all = CASE WHEN NOT EXISTS(\n SELECT 1 FROM group_members gm\n WHERE gm.group_id = messages.group_id AND NOT EXISTS(\n SELECT 1 FROM message_actions ma\n WHERE ma.message_id = messages.message_id\n AND ma.contact_id = gm.contact_id AND ma.type = 'openedAt'\n )\n ) THEN ? ELSE NULL END\n WHERE message_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "02f92beaef5af931c37b62b9ebfbdcf0490b17343ba0e11427f572cc9c41b8b4"
+}
diff --git a/rust/.sqlx/query-03c020a855d64733408f6cf11297505ecaa67c943819bcb3a5ed2a824e668ca6.json b/rust/.sqlx/query-03c020a855d64733408f6cf11297505ecaa67c943819bcb3a5ed2a824e668ca6.json
new file mode 100644
index 00000000..0175749d
--- /dev/null
+++ b/rust/.sqlx/query-03c020a855d64733408f6cf11297505ecaa67c943819bcb3a5ed2a824e668ca6.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM group_members WHERE group_id = ? AND contact_id = ?)",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM group_members WHERE group_id = ? AND contact_id = ?)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "03c020a855d64733408f6cf11297505ecaa67c943819bcb3a5ed2a824e668ca6"
+}
diff --git a/rust/.sqlx/query-051214ce0e6182d160aa7d3f039009d0fb62c946560da627d7de41e57a0d289d.json b/rust/.sqlx/query-051214ce0e6182d160aa7d3f039009d0fb62c946560da627d7de41e57a0d289d.json
new file mode 100644
index 00000000..467a8306
--- /dev/null
+++ b/rust/.sqlx/query-051214ce0e6182d160aa7d3f039009d0fb62c946560da627d7de41e57a0d289d.json
@@ -0,0 +1,86 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT media_id, type AS media_type, download_token,\n encryption_key, encryption_mac, encryption_nonce\n FROM media_files WHERE media_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "media_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "media_id"
+ }
+ }
+ },
+ {
+ "name": "media_type",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "type"
+ }
+ }
+ },
+ {
+ "name": "download_token",
+ "ordinal": 2,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "download_token"
+ }
+ }
+ },
+ {
+ "name": "encryption_key",
+ "ordinal": 3,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "encryption_key"
+ }
+ }
+ },
+ {
+ "name": "encryption_mac",
+ "ordinal": 4,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "encryption_mac"
+ }
+ }
+ },
+ {
+ "name": "encryption_nonce",
+ "ordinal": 5,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "encryption_nonce"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ true,
+ true,
+ true
+ ]
+ },
+ "hash": "051214ce0e6182d160aa7d3f039009d0fb62c946560da627d7de41e57a0d289d"
+}
diff --git a/rust/.sqlx/query-1bf6e82ae32eff099bd28dc8e577247ebef69141bd7c6b887b5b11ed724e11b3.json b/rust/.sqlx/query-06bde3698b9256418111975b79dc581ea72a4f68f7dfd4ceb63bfac45aa031b3.json
similarity index 64%
rename from rust/.sqlx/query-1bf6e82ae32eff099bd28dc8e577247ebef69141bd7c6b887b5b11ed724e11b3.json
rename to rust/.sqlx/query-06bde3698b9256418111975b79dc581ea72a4f68f7dfd4ceb63bfac45aa031b3.json
index 2b0dfb36..8151df35 100644
--- a/rust/.sqlx/query-1bf6e82ae32eff099bd28dc8e577247ebef69141bd7c6b887b5b11ed724e11b3.json
+++ b/rust/.sqlx/query-06bde3698b9256418111975b79dc581ea72a4f68f7dfd4ceb63bfac45aa031b3.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT promotion\n FROM user_discovery_own_promotions\n WHERE version_id > ?\n ",
+ "query": "SELECT promotion FROM user_discovery_own_promotions\n WHERE contact_id = ? ORDER BY version_id DESC LIMIT 1",
"describe": {
"columns": [
{
@@ -22,5 +22,5 @@
false
]
},
- "hash": "1bf6e82ae32eff099bd28dc8e577247ebef69141bd7c6b887b5b11ed724e11b3"
+ "hash": "06bde3698b9256418111975b79dc581ea72a4f68f7dfd4ceb63bfac45aa031b3"
}
diff --git a/rust/.sqlx/query-09817a671f97e3aaeffcf37f8018c231462700155ea0b6b1914a8432cfeb6dcf.json b/rust/.sqlx/query-09817a671f97e3aaeffcf37f8018c231462700155ea0b6b1914a8432cfeb6dcf.json
new file mode 100644
index 00000000..c88e7c4d
--- /dev/null
+++ b/rust/.sqlx/query-09817a671f97e3aaeffcf37f8018c231462700155ea0b6b1914a8432cfeb6dcf.json
@@ -0,0 +1,50 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT sender_id, content, is_deleted_from_sender FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "sender_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "sender_id"
+ }
+ }
+ },
+ {
+ "name": "content",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "content"
+ }
+ }
+ },
+ {
+ "name": "is_deleted_from_sender",
+ "ordinal": 2,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "is_deleted_from_sender"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true,
+ true,
+ false
+ ]
+ },
+ "hash": "09817a671f97e3aaeffcf37f8018c231462700155ea0b6b1914a8432cfeb6dcf"
+}
diff --git a/rust/.sqlx/query-0a000bb74996bb70072c0b0dbb3bd54b690ec20c817a2cb1a29b0ac0fcc884ad.json b/rust/.sqlx/query-0a000bb74996bb70072c0b0dbb3bd54b690ec20c817a2cb1a29b0ac0fcc884ad.json
deleted file mode 100644
index b44300fb..00000000
--- a/rust/.sqlx/query-0a000bb74996bb70072c0b0dbb3bd54b690ec20c817a2cb1a29b0ac0fcc884ad.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO messages(\n group_id,\n message_id,\n sender_id,\n type,\n content,\n quotes_message_id,\n created_at,\n ack_by_server\n ) VALUES (?, ?, ?, 'text', ?, ?, ?, CAST(strftime('%s', 'now') AS INTEGER))\n ON CONFLICT(message_id) DO NOTHING\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 6
- },
- "nullable": []
- },
- "hash": "0a000bb74996bb70072c0b0dbb3bd54b690ec20c817a2cb1a29b0ac0fcc884ad"
-}
diff --git a/rust/.sqlx/query-0a969e410875ea46fde7c9e7fa773b73da80d2a71736532a4bea6dd2efb43cbb.json b/rust/.sqlx/query-0a969e410875ea46fde7c9e7fa773b73da80d2a71736532a4bea6dd2efb43cbb.json
new file mode 100644
index 00000000..61a951c1
--- /dev/null
+++ b/rust/.sqlx/query-0a969e410875ea46fde7c9e7fa773b73da80d2a71736532a4bea6dd2efb43cbb.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO user_discovery_user_relations (\n announced_user_id, from_contact_id, public_key_verified_timestamp\n ) VALUES (?, ?, ?)\n ON CONFLICT(announced_user_id, from_contact_id) DO UPDATE SET\n public_key_verified_timestamp = excluded.public_key_verified_timestamp\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "0a969e410875ea46fde7c9e7fa773b73da80d2a71736532a4bea6dd2efb43cbb"
+}
diff --git a/rust/.sqlx/query-0cf3d4227521bf00d715c1f66b595d9a4943283ab72635c63d184aab40dc19d5.json b/rust/.sqlx/query-0cf3d4227521bf00d715c1f66b595d9a4943283ab72635c63d184aab40dc19d5.json
new file mode 100644
index 00000000..28ad5e74
--- /dev/null
+++ b/rust/.sqlx/query-0cf3d4227521bf00d715c1f66b595d9a4943283ab72635c63d184aab40dc19d5.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM reactions WHERE message_id = ? AND sender_id = ? AND emoji = ?)",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM reactions WHERE message_id = ? AND sender_id = ? AND emoji = ?)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "0cf3d4227521bf00d715c1f66b595d9a4943283ab72635c63d184aab40dc19d5"
+}
diff --git a/rust/.sqlx/query-0dbbfbbf830b11f3e15c85e9e6004196d69f202929f836ac0c0500ea57804dfc.json b/rust/.sqlx/query-0dbbfbbf830b11f3e15c85e9e6004196d69f202929f836ac0c0500ea57804dfc.json
new file mode 100644
index 00000000..3369178e
--- /dev/null
+++ b/rust/.sqlx/query-0dbbfbbf830b11f3e15c85e9e6004196d69f202929f836ac0c0500ea57804dfc.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT display_name FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "display_name",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "display_name"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "0dbbfbbf830b11f3e15c85e9e6004196d69f202929f836ac0c0500ea57804dfc"
+}
diff --git a/rust/.sqlx/query-0dd1dbaa350aa766bada77410aef0da125561fd388fad0b4943f124bc2e73600.json b/rust/.sqlx/query-0dd1dbaa350aa766bada77410aef0da125561fd388fad0b4943f124bc2e73600.json
new file mode 100644
index 00000000..3fb1697b
--- /dev/null
+++ b/rust/.sqlx/query-0dd1dbaa350aa766bada77410aef0da125561fd388fad0b4943f124bc2e73600.json
@@ -0,0 +1,50 @@
+{
+ "db_name": "SQLite",
+ "query": "\n SELECT last_flame_counter_change, flame_counter, max_flame_counter\n FROM groups\n WHERE group_id = ?\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "last_flame_counter_change",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "last_flame_counter_change"
+ }
+ }
+ },
+ {
+ "name": "flame_counter",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "flame_counter"
+ }
+ }
+ },
+ {
+ "name": "max_flame_counter",
+ "ordinal": 2,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "max_flame_counter"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true,
+ false,
+ false
+ ]
+ },
+ "hash": "0dd1dbaa350aa766bada77410aef0da125561fd388fad0b4943f124bc2e73600"
+}
diff --git a/rust/.sqlx/query-0e921f020426da5e0c76cd3c555f37d2abad38c2959ea971ae3085d51ad68ccb.json b/rust/.sqlx/query-0e921f020426da5e0c76cd3c555f37d2abad38c2959ea971ae3085d51ad68ccb.json
new file mode 100644
index 00000000..5bcbeeb0
--- /dev/null
+++ b/rust/.sqlx/query-0e921f020426da5e0c76cd3c555f37d2abad38c2959ea971ae3085d51ad68ccb.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE messages SET content = ?, modified_at = ?\n WHERE message_id = ? AND sender_id = ? AND content IS NOT NULL",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 4
+ },
+ "nullable": []
+ },
+ "hash": "0e921f020426da5e0c76cd3c555f37d2abad38c2959ea971ae3085d51ad68ccb"
+}
diff --git a/rust/.sqlx/query-0ee2e00e444da6707674388c8126e806a8ce02bd0008cc5774ca2d4e11aa5002.json b/rust/.sqlx/query-0ee2e00e444da6707674388c8126e806a8ce02bd0008cc5774ca2d4e11aa5002.json
new file mode 100644
index 00000000..52d5624b
--- /dev/null
+++ b/rust/.sqlx/query-0ee2e00e444da6707674388c8126e806a8ce02bd0008cc5774ca2d4e11aa5002.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT user_id, recovery_contacts_secret_share FROM contacts\n WHERE recovery_contacts_secret_share IS NOT NULL\n AND (recovery_contacts_last_heartbeat IS NULL\n OR recovery_contacts_last_heartbeat <= ?)",
+ "describe": {
+ "columns": [
+ {
+ "name": "user_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_id"
+ }
+ }
+ },
+ {
+ "name": "recovery_contacts_secret_share",
+ "ordinal": 1,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_contacts_secret_share"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ true
+ ]
+ },
+ "hash": "0ee2e00e444da6707674388c8126e806a8ce02bd0008cc5774ca2d4e11aa5002"
+}
diff --git a/rust/.sqlx/query-10facb63e1057c6acfe8b60cfe5f6deb3535eed60977d917f57cae97bb220e30.json b/rust/.sqlx/query-10facb63e1057c6acfe8b60cfe5f6deb3535eed60977d917f57cae97bb220e30.json
new file mode 100644
index 00000000..fc127651
--- /dev/null
+++ b/rust/.sqlx/query-10facb63e1057c6acfe8b60cfe5f6deb3535eed60977d917f57cae97bb220e30.json
@@ -0,0 +1,74 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT group_id, total_media_counter, last_flame_counter_change,\n last_flame_sync, flame_counter\n FROM groups WHERE last_flame_counter_change IS NOT NULL",
+ "describe": {
+ "columns": [
+ {
+ "name": "group_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "group_id"
+ }
+ }
+ },
+ {
+ "name": "total_media_counter",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "total_media_counter"
+ }
+ }
+ },
+ {
+ "name": "last_flame_counter_change",
+ "ordinal": 2,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "last_flame_counter_change"
+ }
+ }
+ },
+ {
+ "name": "last_flame_sync",
+ "ordinal": 3,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "last_flame_sync"
+ }
+ }
+ },
+ {
+ "name": "flame_counter",
+ "ordinal": 4,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "flame_counter"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ true,
+ false
+ ]
+ },
+ "hash": "10facb63e1057c6acfe8b60cfe5f6deb3535eed60977d917f57cae97bb220e30"
+}
diff --git a/rust/.sqlx/query-117582dbddd40553a8a622ed65b24284d8fb91c141ba989e2e3f4846c93d2a4c.json b/rust/.sqlx/query-117582dbddd40553a8a622ed65b24284d8fb91c141ba989e2e3f4846c93d2a4c.json
deleted file mode 100644
index dce8db06..00000000
--- a/rust/.sqlx/query-117582dbddd40553a8a622ed65b24284d8fb91c141ba989e2e3f4846c93d2a4c.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE contacts\n SET username = COALESCE(?, username),\n display_name = ?,\n avatar_svg_compressed = ?,\n sender_profile_counter = COALESCE(?, sender_profile_counter)\n WHERE user_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 5
- },
- "nullable": []
- },
- "hash": "117582dbddd40553a8a622ed65b24284d8fb91c141ba989e2e3f4846c93d2a4c"
-}
diff --git a/rust/.sqlx/query-121378634faf12e63bdd7e116c8a0b24a9e20e39c41e11be8e98b32f7d0e8f61.json b/rust/.sqlx/query-121378634faf12e63bdd7e116c8a0b24a9e20e39c41e11be8e98b32f7d0e8f61.json
new file mode 100644
index 00000000..d1dae8c6
--- /dev/null
+++ b/rust/.sqlx/query-121378634faf12e63bdd7e116c8a0b24a9e20e39c41e11be8e98b32f7d0e8f61.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO receipts(receipt_id, contact_id, message, contact_will_sends_receipt)\n VALUES (?, ?, ?, 0)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "121378634faf12e63bdd7e116c8a0b24a9e20e39c41e11be8e98b32f7d0e8f61"
+}
diff --git a/rust/.sqlx/query-13e31c854a9086ef9fb5ae3a79ad5b915012fed2a79cf7275d74add38acff057.json b/rust/.sqlx/query-13e31c854a9086ef9fb5ae3a79ad5b915012fed2a79cf7275d74add38acff057.json
new file mode 100644
index 00000000..97bacccf
--- /dev/null
+++ b/rust/.sqlx/query-13e31c854a9086ef9fb5ae3a79ad5b915012fed2a79cf7275d74add38acff057.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT media_id FROM messages WHERE message_id = ? AND sender_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "media_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "media_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "13e31c854a9086ef9fb5ae3a79ad5b915012fed2a79cf7275d74add38acff057"
+}
diff --git a/rust/.sqlx/query-141c8905f5d1f47fdbd88a007115dda16edadf2e6f92e7b190e99925870bdc18.json b/rust/.sqlx/query-141c8905f5d1f47fdbd88a007115dda16edadf2e6f92e7b190e99925870bdc18.json
new file mode 100644
index 00000000..f09b4042
--- /dev/null
+++ b/rust/.sqlx/query-141c8905f5d1f47fdbd88a007115dda16edadf2e6f92e7b190e99925870bdc18.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT OR REPLACE INTO receipts(receipt_id, contact_id, message, contact_will_sends_receipt, wake_receiver)\n VALUES (?, ?, ?, ?, ?)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 5
+ },
+ "nullable": []
+ },
+ "hash": "141c8905f5d1f47fdbd88a007115dda16edadf2e6f92e7b190e99925870bdc18"
+}
diff --git a/rust/.sqlx/query-1446c19ba57adcff2aeecf55e73196002e6e58ddd91f9fb7811fd43e67d956c8.json b/rust/.sqlx/query-1446c19ba57adcff2aeecf55e73196002e6e58ddd91f9fb7811fd43e67d956c8.json
new file mode 100644
index 00000000..9bc43079
--- /dev/null
+++ b/rust/.sqlx/query-1446c19ba57adcff2aeecf55e73196002e6e58ddd91f9fb7811fd43e67d956c8.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(\n SELECT 1 FROM group_members\n WHERE group_id = ? AND contact_id = ? AND member_state != 'leftGroup'\n )",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(\n SELECT 1 FROM group_members\n WHERE group_id = ? AND contact_id = ? AND member_state != 'leftGroup'\n )",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "1446c19ba57adcff2aeecf55e73196002e6e58ddd91f9fb7811fd43e67d956c8"
+}
diff --git a/rust/.sqlx/query-8edba8bc58b60b678cb1730572119bd0f1ba1f98232e9d61eb0b7c920a5526f0.json b/rust/.sqlx/query-18c5303b936b2615c4da0bd935b374fabd4973f6cbb5032d76a5d8824c5d2610.json
similarity index 73%
rename from rust/.sqlx/query-8edba8bc58b60b678cb1730572119bd0f1ba1f98232e9d61eb0b7c920a5526f0.json
rename to rust/.sqlx/query-18c5303b936b2615c4da0bd935b374fabd4973f6cbb5032d76a5d8824c5d2610.json
index 9b586c76..db3fd244 100644
--- a/rust/.sqlx/query-8edba8bc58b60b678cb1730572119bd0f1ba1f98232e9d61eb0b7c920a5526f0.json
+++ b/rust/.sqlx/query-18c5303b936b2615c4da0bd935b374fabd4973f6cbb5032d76a5d8824c5d2610.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT media_id\n FROM messages\n WHERE message_id = ? AND sender_id = ?\n ",
+ "query": "\n SELECT media_id\n FROM messages\n WHERE message_id = ?\n ",
"describe": {
"columns": [
{
@@ -16,11 +16,11 @@
}
],
"parameters": {
- "Right": 2
+ "Right": 1
},
"nullable": [
true
]
},
- "hash": "8edba8bc58b60b678cb1730572119bd0f1ba1f98232e9d61eb0b7c920a5526f0"
+ "hash": "18c5303b936b2615c4da0bd935b374fabd4973f6cbb5032d76a5d8824c5d2610"
}
diff --git a/rust/.sqlx/query-18c85d89372ea21ae6cc75750b1c2f8fac5d97ad75b1beb452176b36d3fbcaa7.json b/rust/.sqlx/query-18c85d89372ea21ae6cc75750b1c2f8fac5d97ad75b1beb452176b36d3fbcaa7.json
new file mode 100644
index 00000000..2857769e
--- /dev/null
+++ b/rust/.sqlx/query-18c85d89372ea21ae6cc75750b1c2f8fac5d97ad75b1beb452176b36d3fbcaa7.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT accepted, requested FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "accepted",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "accepted"
+ }
+ }
+ },
+ {
+ "name": "requested",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "requested"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false
+ ]
+ },
+ "hash": "18c85d89372ea21ae6cc75750b1c2f8fac5d97ad75b1beb452176b36d3fbcaa7"
+}
diff --git a/rust/.sqlx/query-19645cdadf2646e2c3afb1f09dd509a62222baebc54743f3485368fdf27c167a.json b/rust/.sqlx/query-19645cdadf2646e2c3afb1f09dd509a62222baebc54743f3485368fdf27c167a.json
new file mode 100644
index 00000000..02b99fe6
--- /dev/null
+++ b/rust/.sqlx/query-19645cdadf2646e2c3afb1f09dd509a62222baebc54743f3485368fdf27c167a.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO media_files(media_id, type, download_state, upload_state) VALUES (?, 'image', 'ready', 'uploaded')",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "19645cdadf2646e2c3afb1f09dd509a62222baebc54743f3485368fdf27c167a"
+}
diff --git a/rust/.sqlx/query-19af16bcf6c54900ed6032ce8ed7b5ef9d71456488805e343c2af10ad471b22a.json b/rust/.sqlx/query-19af16bcf6c54900ed6032ce8ed7b5ef9d71456488805e343c2af10ad471b22a.json
new file mode 100644
index 00000000..933c8a28
--- /dev/null
+++ b/rust/.sqlx/query-19af16bcf6c54900ed6032ce8ed7b5ef9d71456488805e343c2af10ad471b22a.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT OR IGNORE INTO notification_outbox(\n event_id, notification_id, conversation_id, sender_id,\n message_id, kind, content, created_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 8
+ },
+ "nullable": []
+ },
+ "hash": "19af16bcf6c54900ed6032ce8ed7b5ef9d71456488805e343c2af10ad471b22a"
+}
diff --git a/rust/.sqlx/query-1acafcad398cb31491dcee91a005eddd33a03d40afa505b7e0b5f59e2f731a20.json b/rust/.sqlx/query-1acafcad398cb31491dcee91a005eddd33a03d40afa505b7e0b5f59e2f731a20.json
deleted file mode 100644
index a9746e54..00000000
--- a/rust/.sqlx/query-1acafcad398cb31491dcee91a005eddd33a03d40afa505b7e0b5f59e2f731a20.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE receipts\n SET mark_for_retry = CAST(strftime('%s', 'now') AS INTEGER),\n retry_count = retry_count + 1,\n ack_by_server_at = NULL\n WHERE receipt_id = ? AND contact_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "1acafcad398cb31491dcee91a005eddd33a03d40afa505b7e0b5f59e2f731a20"
-}
diff --git a/rust/.sqlx/query-1be4155879419ae7cdcd7f8774ea5a6cce7ecb05e825095f4fffd6aaf4288d18.json b/rust/.sqlx/query-1be4155879419ae7cdcd7f8774ea5a6cce7ecb05e825095f4fffd6aaf4288d18.json
deleted file mode 100644
index 91b16f1f..00000000
--- a/rust/.sqlx/query-1be4155879419ae7cdcd7f8774ea5a6cce7ecb05e825095f4fffd6aaf4288d18.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO user_discovery_user_relations (\n announced_user_id,\n from_contact_id,\n public_key_verified_timestamp\n ) VALUES (?, ?, ?)\n ON CONFLICT(announced_user_id, from_contact_id) DO UPDATE SET\n public_key_verified_timestamp = excluded.public_key_verified_timestamp\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 3
- },
- "nullable": []
- },
- "hash": "1be4155879419ae7cdcd7f8774ea5a6cce7ecb05e825095f4fffd6aaf4288d18"
-}
diff --git a/rust/.sqlx/query-1d8a2e974208c2ca67e4fe792c4aedcaf5bb86dc24f09ed71069d3868df833ee.json b/rust/.sqlx/query-1d8a2e974208c2ca67e4fe792c4aedcaf5bb86dc24f09ed71069d3868df833ee.json
new file mode 100644
index 00000000..cf778f79
--- /dev/null
+++ b/rust/.sqlx/query-1d8a2e974208c2ca67e4fe792c4aedcaf5bb86dc24f09ed71069d3868df833ee.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT last_type_indicator FROM group_members WHERE group_id = ? AND contact_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "last_type_indicator",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "group_members",
+ "name": "last_type_indicator"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "1d8a2e974208c2ca67e4fe792c4aedcaf5bb86dc24f09ed71069d3868df833ee"
+}
diff --git a/rust/.sqlx/query-1dad19878770f615fde37f4fa8ce4cb8254a723c4d353a2ccc1d9d224bb68971.json b/rust/.sqlx/query-1dad19878770f615fde37f4fa8ce4cb8254a723c4d353a2ccc1d9d224bb68971.json
new file mode 100644
index 00000000..0d7b40d4
--- /dev/null
+++ b/rust/.sqlx/query-1dad19878770f615fde37f4fa8ce4cb8254a723c4d353a2ccc1d9d224bb68971.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT upload_state FROM media_files WHERE media_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "upload_state",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "upload_state"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "1dad19878770f615fde37f4fa8ce4cb8254a723c4d353a2ccc1d9d224bb68971"
+}
diff --git a/rust/.sqlx/query-1e27aa2db6de33b41610d8fe9394b7d54224d8717dd687516da0ced5268e4899.json b/rust/.sqlx/query-1e27aa2db6de33b41610d8fe9394b7d54224d8717dd687516da0ced5268e4899.json
new file mode 100644
index 00000000..cc1d13ee
--- /dev/null
+++ b/rust/.sqlx/query-1e27aa2db6de33b41610d8fe9394b7d54224d8717dd687516da0ced5268e4899.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT recovery_last_heartbeat FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "recovery_last_heartbeat",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_last_heartbeat"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "1e27aa2db6de33b41610d8fe9394b7d54224d8717dd687516da0ced5268e4899"
+}
diff --git a/rust/.sqlx/query-1f22026fd1c3dc141303b1534b4d8fc497dc5fab1685aa8999f9ec9056b38df7.json b/rust/.sqlx/query-1f22026fd1c3dc141303b1534b4d8fc497dc5fab1685aa8999f9ec9056b38df7.json
new file mode 100644
index 00000000..b10edc84
--- /dev/null
+++ b/rust/.sqlx/query-1f22026fd1c3dc141303b1534b4d8fc497dc5fab1685aa8999f9ec9056b38df7.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(\n SELECT 1 FROM user_discovery_own_promotions\n WHERE contact_id = ? AND length(promotion) > 0\n )",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(\n SELECT 1 FROM user_discovery_own_promotions\n WHERE contact_id = ? AND length(promotion) > 0\n )",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "1f22026fd1c3dc141303b1534b4d8fc497dc5fab1685aa8999f9ec9056b38df7"
+}
diff --git a/rust/.sqlx/query-1f8ab52f86c62c865231b13253c61b6992a527e9fd1d89f074bed821fcb9095d.json b/rust/.sqlx/query-1f8ab52f86c62c865231b13253c61b6992a527e9fd1d89f074bed821fcb9095d.json
new file mode 100644
index 00000000..980a9ec0
--- /dev/null
+++ b/rust/.sqlx/query-1f8ab52f86c62c865231b13253c61b6992a527e9fd1d89f074bed821fcb9095d.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET recovery_secret_share = ?, recovery_is_trusted_friend = 1 WHERE user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "1f8ab52f86c62c865231b13253c61b6992a527e9fd1d89f074bed821fcb9095d"
+}
diff --git a/rust/.sqlx/query-2136bd64733c0268d4da52cf36db8df3533d34a45008bddf61e1090ae1ace885.json b/rust/.sqlx/query-2136bd64733c0268d4da52cf36db8df3533d34a45008bddf61e1090ae1ace885.json
deleted file mode 100644
index 53238ca7..00000000
--- a/rust/.sqlx/query-2136bd64733c0268d4da52cf36db8df3533d34a45008bddf61e1090ae1ace885.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO received_receipts(receipt_id)\n VALUES (?)\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 1
- },
- "nullable": []
- },
- "hash": "2136bd64733c0268d4da52cf36db8df3533d34a45008bddf61e1090ae1ace885"
-}
diff --git a/rust/.sqlx/query-2138b7b197c5637580e0aa98d5e2d7e80b0cc1b660ab96ace53bb90ea5ff2968.json b/rust/.sqlx/query-2138b7b197c5637580e0aa98d5e2d7e80b0cc1b660ab96ace53bb90ea5ff2968.json
new file mode 100644
index 00000000..39e82bdf
--- /dev/null
+++ b/rust/.sqlx/query-2138b7b197c5637580e0aa98d5e2d7e80b0cc1b660ab96ace53bb90ea5ff2968.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET requested = 1, deleted_by_user = 0 WHERE user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "2138b7b197c5637580e0aa98d5e2d7e80b0cc1b660ab96ace53bb90ea5ff2968"
+}
diff --git a/rust/.sqlx/query-225cd55439cc89f0ee7bc53a0f19cf53a98ec2bdc32c82e43c6beef3bdc23d98.json b/rust/.sqlx/query-225cd55439cc89f0ee7bc53a0f19cf53a98ec2bdc32c82e43c6beef3bdc23d98.json
new file mode 100644
index 00000000..d2ef958a
--- /dev/null
+++ b/rust/.sqlx/query-225cd55439cc89f0ee7bc53a0f19cf53a98ec2bdc32c82e43c6beef3bdc23d98.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM api_outbox WHERE sequence_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "225cd55439cc89f0ee7bc53a0f19cf53a98ec2bdc32c82e43c6beef3bdc23d98"
+}
diff --git a/rust/.sqlx/query-23ce3aa70fcb31de3c6e6b1ba99750488d5d732b65ee326ca5b5a1626d43155c.json b/rust/.sqlx/query-23ce3aa70fcb31de3c6e6b1ba99750488d5d732b65ee326ca5b5a1626d43155c.json
new file mode 100644
index 00000000..0737fbd1
--- /dev/null
+++ b/rust/.sqlx/query-23ce3aa70fcb31de3c6e6b1ba99750488d5d732b65ee326ca5b5a1626d43155c.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE groups SET draft_message = 'draft text' WHERE group_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "23ce3aa70fcb31de3c6e6b1ba99750488d5d732b65ee326ca5b5a1626d43155c"
+}
diff --git a/rust/.sqlx/query-274f8f246a13f484fd1941bb0e570a06a7db075f26dfe0c943aefa0d66cff7aa.json b/rust/.sqlx/query-274f8f246a13f484fd1941bb0e570a06a7db075f26dfe0c943aefa0d66cff7aa.json
new file mode 100644
index 00000000..7ec7b931
--- /dev/null
+++ b/rust/.sqlx/query-274f8f246a13f484fd1941bb0e570a06a7db075f26dfe0c943aefa0d66cff7aa.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(*) FROM receipts WHERE contact_id = 9",
+ "describe": {
+ "columns": [
+ {
+ "name": "COUNT(*)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "274f8f246a13f484fd1941bb0e570a06a7db075f26dfe0c943aefa0d66cff7aa"
+}
diff --git a/rust/.sqlx/query-28663e0e60055c3bef26992fecdaed69a4ae46aa385d8927a62504f838987dfc.json b/rust/.sqlx/query-28663e0e60055c3bef26992fecdaed69a4ae46aa385d8927a62504f838987dfc.json
new file mode 100644
index 00000000..7e2b1b31
--- /dev/null
+++ b/rust/.sqlx/query-28663e0e60055c3bef26992fecdaed69a4ae46aa385d8927a62504f838987dfc.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO group_members(group_id, contact_id, member_state) VALUES (?, ?, ?)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "28663e0e60055c3bef26992fecdaed69a4ae46aa385d8927a62504f838987dfc"
+}
diff --git a/rust/.sqlx/query-291bf3c6dfb80861e3a250243693beff0798bc51759ea525dae477bff2096585.json b/rust/.sqlx/query-291bf3c6dfb80861e3a250243693beff0798bc51759ea525dae477bff2096585.json
new file mode 100644
index 00000000..b5585c71
--- /dev/null
+++ b/rust/.sqlx/query-291bf3c6dfb80861e3a250243693beff0798bc51759ea525dae477bff2096585.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO group_histories(group_history_id, group_id, contact_id, type)\n VALUES (?, ?, ?, 'addMember')",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "291bf3c6dfb80861e3a250243693beff0798bc51759ea525dae477bff2096585"
+}
diff --git a/rust/.sqlx/query-29bd81e76831e95b2a26531cbcf59139c1954d23158600ace4b314fcd4626992.json b/rust/.sqlx/query-29bd81e76831e95b2a26531cbcf59139c1954d23158600ace4b314fcd4626992.json
new file mode 100644
index 00000000..2f10056a
--- /dev/null
+++ b/rust/.sqlx/query-29bd81e76831e95b2a26531cbcf59139c1954d23158600ace4b314fcd4626992.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT contact_id FROM group_members WHERE group_id = ? AND group_public_key = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "contact_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "group_members",
+ "name": "contact_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "29bd81e76831e95b2a26531cbcf59139c1954d23158600ace4b314fcd4626992"
+}
diff --git a/rust/.sqlx/query-2a923146a880d7f8684495e93324a743ae58b3f32e01e5ad1fa5a304bb535ae5.json b/rust/.sqlx/query-2a923146a880d7f8684495e93324a743ae58b3f32e01e5ad1fa5a304bb535ae5.json
new file mode 100644
index 00000000..9f9f5f43
--- /dev/null
+++ b/rust/.sqlx/query-2a923146a880d7f8684495e93324a743ae58b3f32e01e5ad1fa5a304bb535ae5.json
@@ -0,0 +1,98 @@
+{
+ "db_name": "SQLite",
+ "query": "\n SELECT r.contact_id, r.message, r.message_id, r.contact_will_sends_receipt,\n r.wake_receiver, c.account_deleted, c.signal_version\n FROM receipts r\n JOIN contacts c ON c.user_id = r.contact_id\n WHERE r.receipt_id = ?\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "contact_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "receipts",
+ "name": "contact_id"
+ }
+ }
+ },
+ {
+ "name": "message",
+ "ordinal": 1,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "receipts",
+ "name": "message"
+ }
+ }
+ },
+ {
+ "name": "message_id",
+ "ordinal": 2,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "receipts",
+ "name": "message_id"
+ }
+ }
+ },
+ {
+ "name": "contact_will_sends_receipt",
+ "ordinal": 3,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "receipts",
+ "name": "contact_will_sends_receipt"
+ }
+ }
+ },
+ {
+ "name": "wake_receiver",
+ "ordinal": 4,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "receipts",
+ "name": "wake_receiver"
+ }
+ }
+ },
+ {
+ "name": "account_deleted",
+ "ordinal": 5,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "account_deleted"
+ }
+ }
+ },
+ {
+ "name": "signal_version",
+ "ordinal": 6,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "signal_version"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ false,
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "2a923146a880d7f8684495e93324a743ae58b3f32e01e5ad1fa5a304bb535ae5"
+}
diff --git a/rust/.sqlx/query-2b86ca80f38f47f115c8c8e6ce79343574e6ca435946c303dd8f35e68bfc57ef.json b/rust/.sqlx/query-2b86ca80f38f47f115c8c8e6ce79343574e6ca435946c303dd8f35e68bfc57ef.json
new file mode 100644
index 00000000..20ac15a0
--- /dev/null
+++ b/rust/.sqlx/query-2b86ca80f38f47f115c8c8e6ce79343574e6ca435946c303dd8f35e68bfc57ef.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE media_files SET type = ?, download_state = 'pending',\n requires_authentication = ?, display_limit_in_milliseconds = ?,\n download_token = ?, encryption_key = ?, encryption_mac = ?,\n encryption_nonce = ?, created_at = ? WHERE media_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 9
+ },
+ "nullable": []
+ },
+ "hash": "2b86ca80f38f47f115c8c8e6ce79343574e6ca435946c303dd8f35e68bfc57ef"
+}
diff --git a/rust/.sqlx/query-2c4a9f54ba414124c446f52333a1c2dcf88c8ad4d4d96f746ce47532452ec19d.json b/rust/.sqlx/query-2c4a9f54ba414124c446f52333a1c2dcf88c8ad4d4d96f746ce47532452ec19d.json
new file mode 100644
index 00000000..c1863f19
--- /dev/null
+++ b/rust/.sqlx/query-2c4a9f54ba414124c446f52333a1c2dcf88c8ad4d4d96f746ce47532452ec19d.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET recovery_contacts_last_heartbeat = ? WHERE user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "2c4a9f54ba414124c446f52333a1c2dcf88c8ad4d4d96f746ce47532452ec19d"
+}
diff --git a/rust/.sqlx/query-2cc4a73378cf37ff6e3dcbd761fec56686a8f9820a3bb2c489e94fb036de0786.json b/rust/.sqlx/query-2cc4a73378cf37ff6e3dcbd761fec56686a8f9820a3bb2c489e94fb036de0786.json
new file mode 100644
index 00000000..cfdc6b73
--- /dev/null
+++ b/rust/.sqlx/query-2cc4a73378cf37ff6e3dcbd761fec56686a8f9820a3bb2c489e94fb036de0786.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE media_files SET download_state = 'reuploadRequested' WHERE media_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "2cc4a73378cf37ff6e3dcbd761fec56686a8f9820a3bb2c489e94fb036de0786"
+}
diff --git a/rust/.sqlx/query-2e24484b81be3b3a726c018b26311d543af677422a69318c0e187c3cf7379bf7.json b/rust/.sqlx/query-2e24484b81be3b3a726c018b26311d543af677422a69318c0e187c3cf7379bf7.json
new file mode 100644
index 00000000..dc686293
--- /dev/null
+++ b/rust/.sqlx/query-2e24484b81be3b3a726c018b26311d543af677422a69318c0e187c3cf7379bf7.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO group_members(group_id, contact_id, member_state)\n VALUES (?, ?, ?)\n ON CONFLICT(group_id, contact_id)\n DO UPDATE SET member_state = excluded.member_state\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "2e24484b81be3b3a726c018b26311d543af677422a69318c0e187c3cf7379bf7"
+}
diff --git a/rust/.sqlx/query-2e5c2ccc9be071bcebcb9c8a7b0564e6ef21a38106bb9bc90c840ac1903b75d5.json b/rust/.sqlx/query-2e5c2ccc9be071bcebcb9c8a7b0564e6ef21a38106bb9bc90c840ac1903b75d5.json
new file mode 100644
index 00000000..25ec7153
--- /dev/null
+++ b/rust/.sqlx/query-2e5c2ccc9be071bcebcb9c8a7b0564e6ef21a38106bb9bc90c840ac1903b75d5.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT contact_id FROM group_members\n WHERE group_id = ? AND (member_state IS NULL OR member_state != 'leftGroup')",
+ "describe": {
+ "columns": [
+ {
+ "name": "contact_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "group_members",
+ "name": "contact_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "2e5c2ccc9be071bcebcb9c8a7b0564e6ef21a38106bb9bc90c840ac1903b75d5"
+}
diff --git a/rust/.sqlx/query-2ec060bf87e7c0a76c47be0010f9f9c37d20f2ea3ae1da27c757a5d32b60a1cc.json b/rust/.sqlx/query-2ec060bf87e7c0a76c47be0010f9f9c37d20f2ea3ae1da27c757a5d32b60a1cc.json
new file mode 100644
index 00000000..64325dc0
--- /dev/null
+++ b/rust/.sqlx/query-2ec060bf87e7c0a76c47be0010f9f9c37d20f2ea3ae1da27c757a5d32b60a1cc.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT group_name FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "group_name",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "group_name"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "2ec060bf87e7c0a76c47be0010f9f9c37d20f2ea3ae1da27c757a5d32b60a1cc"
+}
diff --git a/rust/.sqlx/query-2f1854f10ac694a9dcabe133c32d986978bf168dccce04d461beeef220f8952c.json b/rust/.sqlx/query-2f1854f10ac694a9dcabe133c32d986978bf168dccce04d461beeef220f8952c.json
new file mode 100644
index 00000000..efb863da
--- /dev/null
+++ b/rust/.sqlx/query-2f1854f10ac694a9dcabe133c32d986978bf168dccce04d461beeef220f8952c.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "\n SELECT EXISTS(\n SELECT 1\n FROM key_verifications AS verification\n WHERE verification.contact_id = ?\n AND (\n verification.type != 'contactSharedByVerified'\n OR EXISTS(\n SELECT 1 FROM key_verifications AS verifier_verification\n WHERE verifier_verification.contact_id = verification.verified_by\n )\n )\n )\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(\n SELECT 1\n FROM key_verifications AS verification\n WHERE verification.contact_id = ?\n AND (\n verification.type != 'contactSharedByVerified'\n OR EXISTS(\n SELECT 1 FROM key_verifications AS verifier_verification\n WHERE verifier_verification.contact_id = verification.verified_by\n )\n )\n )",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "2f1854f10ac694a9dcabe133c32d986978bf168dccce04d461beeef220f8952c"
+}
diff --git a/rust/.sqlx/query-2fec92a566a99026de111e1b9b175719cb54932ea03bd754747407d3291f764d.json b/rust/.sqlx/query-2fec92a566a99026de111e1b9b175719cb54932ea03bd754747407d3291f764d.json
new file mode 100644
index 00000000..85d5e2d8
--- /dev/null
+++ b/rust/.sqlx/query-2fec92a566a99026de111e1b9b175719cb54932ea03bd754747407d3291f764d.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO message_histories(message_id, content, created_at)\n SELECT message_id, content, ? FROM messages\n WHERE message_id = ? AND sender_id = ? AND content IS NOT NULL",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "2fec92a566a99026de111e1b9b175719cb54932ea03bd754747407d3291f764d"
+}
diff --git a/rust/.sqlx/query-5988df8ec10b625bbd16ddcd2dccad54f2b209e74849509c1905e0f106756c7d.json b/rust/.sqlx/query-345381ad177426da0800eaee0c04352e78377c106d7e156c07aef00858bc8bc8.json
similarity index 65%
rename from rust/.sqlx/query-5988df8ec10b625bbd16ddcd2dccad54f2b209e74849509c1905e0f106756c7d.json
rename to rust/.sqlx/query-345381ad177426da0800eaee0c04352e78377c106d7e156c07aef00858bc8bc8.json
index 2d65b321..d8be76f3 100644
--- a/rust/.sqlx/query-5988df8ec10b625bbd16ddcd2dccad54f2b209e74849509c1905e0f106756c7d.json
+++ b/rust/.sqlx/query-345381ad177426da0800eaee0c04352e78377c106d7e156c07aef00858bc8bc8.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT sender_id\n FROM messages\n WHERE message_id = ?\n ",
+ "query": "\n SELECT sender_id\n FROM messages\n WHERE message_id = ?\n ",
"describe": {
"columns": [
{
@@ -22,5 +22,5 @@
true
]
},
- "hash": "5988df8ec10b625bbd16ddcd2dccad54f2b209e74849509c1905e0f106756c7d"
+ "hash": "345381ad177426da0800eaee0c04352e78377c106d7e156c07aef00858bc8bc8"
}
diff --git a/rust/.sqlx/query-3462ce3b677445a641de96634d6f69a661f2b58cbfff84eac55eaec901a54284.json b/rust/.sqlx/query-3462ce3b677445a641de96634d6f69a661f2b58cbfff84eac55eaec901a54284.json
deleted file mode 100644
index 87b9cc82..00000000
--- a/rust/.sqlx/query-3462ce3b677445a641de96634d6f69a661f2b58cbfff84eac55eaec901a54284.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE contacts\n SET user_discovery_version = ?\n WHERE user_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "3462ce3b677445a641de96634d6f69a661f2b58cbfff84eac55eaec901a54284"
-}
diff --git a/rust/.sqlx/query-370baafebd6f09754b0e29cf2b384c07426a989e8b1299414d6f5e7a9cdccadb.json b/rust/.sqlx/query-370baafebd6f09754b0e29cf2b384c07426a989e8b1299414d6f5e7a9cdccadb.json
deleted file mode 100644
index 5da40a79..00000000
--- a/rust/.sqlx/query-370baafebd6f09754b0e29cf2b384c07426a989e8b1299414d6f5e7a9cdccadb.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE contacts\n SET recovery_secret_share = ?\n WHERE user_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "370baafebd6f09754b0e29cf2b384c07426a989e8b1299414d6f5e7a9cdccadb"
-}
diff --git a/rust/.sqlx/query-3814aef32753b08f1455d22511d11bff65e50a32861ccb8b8323e008e114a8cc.json b/rust/.sqlx/query-3814aef32753b08f1455d22511d11bff65e50a32861ccb8b8323e008e114a8cc.json
new file mode 100644
index 00000000..dcedf377
--- /dev/null
+++ b/rust/.sqlx/query-3814aef32753b08f1455d22511d11bff65e50a32861ccb8b8323e008e114a8cc.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT left_group FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "left_group",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "left_group"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "3814aef32753b08f1455d22511d11bff65e50a32861ccb8b8323e008e114a8cc"
+}
diff --git a/rust/.sqlx/query-3b22d58da61061edeccdd6188f947bf77eef678b7e378e17effbb3ce0fd7f3a9.json b/rust/.sqlx/query-3b22d58da61061edeccdd6188f947bf77eef678b7e378e17effbb3ce0fd7f3a9.json
new file mode 100644
index 00000000..debad067
--- /dev/null
+++ b/rust/.sqlx/query-3b22d58da61061edeccdd6188f947bf77eef678b7e378e17effbb3ce0fd7f3a9.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO key_verifications(contact_id, type) VALUES (?, 'manualTest')",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "3b22d58da61061edeccdd6188f947bf77eef678b7e378e17effbb3ce0fd7f3a9"
+}
diff --git a/rust/.sqlx/query-3b23ab2f5440c3ba2ccb650d6b961bdfcfd1a9f3f2128bc0abdc47ab637774e3.json b/rust/.sqlx/query-3b23ab2f5440c3ba2ccb650d6b961bdfcfd1a9f3f2128bc0abdc47ab637774e3.json
new file mode 100644
index 00000000..65f431d7
--- /dev/null
+++ b/rust/.sqlx/query-3b23ab2f5440c3ba2ccb650d6b961bdfcfd1a9f3f2128bc0abdc47ab637774e3.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE groups SET left_group = ? WHERE group_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "3b23ab2f5440c3ba2ccb650d6b961bdfcfd1a9f3f2128bc0abdc47ab637774e3"
+}
diff --git a/rust/.sqlx/query-3c0f58c288fae9fcc8dcaae6d1d48a859644a0e5d63e16e146265f9afbb11a00.json b/rust/.sqlx/query-3c0f58c288fae9fcc8dcaae6d1d48a859644a0e5d63e16e146265f9afbb11a00.json
new file mode 100644
index 00000000..8628e663
--- /dev/null
+++ b/rust/.sqlx/query-3c0f58c288fae9fcc8dcaae6d1d48a859644a0e5d63e16e146265f9afbb11a00.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO user_discovery_announced_users (\n announced_user_id, announced_public_key, public_id\n ) VALUES (?, ?, ?)\n ON CONFLICT DO UPDATE SET\n announced_user_id = excluded.announced_user_id,\n announced_public_key = excluded.announced_public_key,\n public_id = excluded.public_id\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "3c0f58c288fae9fcc8dcaae6d1d48a859644a0e5d63e16e146265f9afbb11a00"
+}
diff --git a/rust/.sqlx/query-3c5c07a73316e7e31ef06c5a6c5b273cb4e2665bd78cb5cbc38b73447af9eaf2.json b/rust/.sqlx/query-3c5c07a73316e7e31ef06c5a6c5b273cb4e2665bd78cb5cbc38b73447af9eaf2.json
new file mode 100644
index 00000000..3d0491ec
--- /dev/null
+++ b/rust/.sqlx/query-3c5c07a73316e7e31ef06c5a6c5b273cb4e2665bd78cb5cbc38b73447af9eaf2.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT sequence_id, payload FROM api_outbox ORDER BY created_at",
+ "describe": {
+ "columns": [
+ {
+ "name": "sequence_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "api_outbox",
+ "name": "sequence_id"
+ }
+ }
+ },
+ {
+ "name": "payload",
+ "ordinal": 1,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "api_outbox",
+ "name": "payload"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false,
+ false
+ ]
+ },
+ "hash": "3c5c07a73316e7e31ef06c5a6c5b273cb4e2665bd78cb5cbc38b73447af9eaf2"
+}
diff --git a/rust/.sqlx/query-3cac7888e450d5d16693a96c3f46bdfeaf3e0b2237f33e0855a8dfc7895fd9a3.json b/rust/.sqlx/query-3cac7888e450d5d16693a96c3f46bdfeaf3e0b2237f33e0855a8dfc7895fd9a3.json
new file mode 100644
index 00000000..95eb6a74
--- /dev/null
+++ b/rust/.sqlx/query-3cac7888e450d5d16693a96c3f46bdfeaf3e0b2237f33e0855a8dfc7895fd9a3.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO key_verifications(contact_id, type, verified_by)\n VALUES (?, ?, ?)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "3cac7888e450d5d16693a96c3f46bdfeaf3e0b2237f33e0855a8dfc7895fd9a3"
+}
diff --git a/rust/.sqlx/query-3d1fdb8baa14a261fd2e2943ce5712ed95f12e337e88c18619aed086ebbd42aa.json b/rust/.sqlx/query-3d1fdb8baa14a261fd2e2943ce5712ed95f12e337e88c18619aed086ebbd42aa.json
new file mode 100644
index 00000000..e84cb676
--- /dev/null
+++ b/rust/.sqlx/query-3d1fdb8baa14a261fd2e2943ce5712ed95f12e337e88c18619aed086ebbd42aa.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE groups\n SET last_message_exchange = MAX(last_message_exchange, ?)\n WHERE group_id = ?\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "3d1fdb8baa14a261fd2e2943ce5712ed95f12e337e88c18619aed086ebbd42aa"
+}
diff --git a/rust/.sqlx/query-174e1f8d22842961cc26633c7f715a0c278dcda82838188586821354c306b1e9.json b/rust/.sqlx/query-3dc2f59650c7770eaf991ff1e0ba39c24e06317ed83dd4e20bd429c4ff802ab0.json
similarity index 54%
rename from rust/.sqlx/query-174e1f8d22842961cc26633c7f715a0c278dcda82838188586821354c306b1e9.json
rename to rust/.sqlx/query-3dc2f59650c7770eaf991ff1e0ba39c24e06317ed83dd4e20bd429c4ff802ab0.json
index c51fffc1..14f11223 100644
--- a/rust/.sqlx/query-174e1f8d22842961cc26633c7f715a0c278dcda82838188586821354c306b1e9.json
+++ b/rust/.sqlx/query-3dc2f59650c7770eaf991ff1e0ba39c24e06317ed83dd4e20bd429c4ff802ab0.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT COUNT(*)\n FROM reactions\n WHERE message_id = 'path-text-message' AND sender_id = ? AND emoji = '👍'\n ",
+ "query": "SELECT COUNT(*) FROM messages WHERE media_id = ?",
"describe": {
"columns": [
{
@@ -17,5 +17,5 @@
false
]
},
- "hash": "174e1f8d22842961cc26633c7f715a0c278dcda82838188586821354c306b1e9"
+ "hash": "3dc2f59650c7770eaf991ff1e0ba39c24e06317ed83dd4e20bd429c4ff802ab0"
}
diff --git a/rust/.sqlx/query-405bd3fad9cb5b677e1354c27453e3d4afe7c79858ea617337036717f6fea03a.json b/rust/.sqlx/query-405bd3fad9cb5b677e1354c27453e3d4afe7c79858ea617337036717f6fea03a.json
new file mode 100644
index 00000000..649f30fc
--- /dev/null
+++ b/rust/.sqlx/query-405bd3fad9cb5b677e1354c27453e3d4afe7c79858ea617337036717f6fea03a.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT my_group_private_key FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "my_group_private_key",
+ "ordinal": 0,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "my_group_private_key"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "405bd3fad9cb5b677e1354c27453e3d4afe7c79858ea617337036717f6fea03a"
+}
diff --git a/rust/.sqlx/query-408e8205bae9e1640fbaf2f91eef3e3896323fce641a76b9cab489cd40e4506c.json b/rust/.sqlx/query-408e8205bae9e1640fbaf2f91eef3e3896323fce641a76b9cab489cd40e4506c.json
new file mode 100644
index 00000000..82c5d2fb
--- /dev/null
+++ b/rust/.sqlx/query-408e8205bae9e1640fbaf2f91eef3e3896323fce641a76b9cab489cd40e4506c.json
@@ -0,0 +1,74 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT group_id, group_name, state_version_id,\n state_encryption_key, my_group_private_key\n FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "group_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "group_id"
+ }
+ }
+ },
+ {
+ "name": "group_name",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "group_name"
+ }
+ }
+ },
+ {
+ "name": "state_version_id",
+ "ordinal": 2,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "state_version_id"
+ }
+ }
+ },
+ {
+ "name": "state_encryption_key",
+ "ordinal": 3,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "state_encryption_key"
+ }
+ }
+ },
+ {
+ "name": "my_group_private_key",
+ "ordinal": 4,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "my_group_private_key"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ true,
+ true
+ ]
+ },
+ "hash": "408e8205bae9e1640fbaf2f91eef3e3896323fce641a76b9cab489cd40e4506c"
+}
diff --git a/rust/.sqlx/query-411a854abb8d23c104b7290c25313c679ef0361348c17d2ee740d30ad8c5a424.json b/rust/.sqlx/query-411a854abb8d23c104b7290c25313c679ef0361348c17d2ee740d30ad8c5a424.json
new file mode 100644
index 00000000..52de7ccd
--- /dev/null
+++ b/rust/.sqlx/query-411a854abb8d23c104b7290c25313c679ef0361348c17d2ee740d30ad8c5a424.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE group_members SET last_message = CAST(strftime('%s','now') AS INTEGER) WHERE group_id = ? AND contact_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "411a854abb8d23c104b7290c25313c679ef0361348c17d2ee740d30ad8c5a424"
+}
diff --git a/rust/.sqlx/query-f939d4c2915e167dbcbac32fe36c5caa4719f1a75693e6e2b6bb804ea9f790ed.json b/rust/.sqlx/query-413b5a5068e17f5ba2c8bb958f51108794c944e8948d0589c21c510d010e5bd6.json
similarity index 62%
rename from rust/.sqlx/query-f939d4c2915e167dbcbac32fe36c5caa4719f1a75693e6e2b6bb804ea9f790ed.json
rename to rust/.sqlx/query-413b5a5068e17f5ba2c8bb958f51108794c944e8948d0589c21c510d010e5bd6.json
index 50aee5cc..0bde8938 100644
--- a/rust/.sqlx/query-f939d4c2915e167dbcbac32fe36c5caa4719f1a75693e6e2b6bb804ea9f790ed.json
+++ b/rust/.sqlx/query-413b5a5068e17f5ba2c8bb958f51108794c944e8948d0589c21c510d010e5bd6.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT share\n FROM user_discovery_shares\n WHERE contact_id = ?\n LIMIT 1\n ",
+ "query": "SELECT share FROM user_discovery_shares WHERE contact_id = ? LIMIT 1",
"describe": {
"columns": [
{
@@ -22,5 +22,5 @@
false
]
},
- "hash": "f939d4c2915e167dbcbac32fe36c5caa4719f1a75693e6e2b6bb804ea9f790ed"
+ "hash": "413b5a5068e17f5ba2c8bb958f51108794c944e8948d0589c21c510d010e5bd6"
}
diff --git a/rust/.sqlx/query-41577de24c37ee6af005567456b7df22c28ebdbcd94070e924fd9c0b39725873.json b/rust/.sqlx/query-41577de24c37ee6af005567456b7df22c28ebdbcd94070e924fd9c0b39725873.json
new file mode 100644
index 00000000..350659fd
--- /dev/null
+++ b/rust/.sqlx/query-41577de24c37ee6af005567456b7df22c28ebdbcd94070e924fd9c0b39725873.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM message_histories WHERE message_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "41577de24c37ee6af005567456b7df22c28ebdbcd94070e924fd9c0b39725873"
+}
diff --git a/rust/.sqlx/query-44196efd5f9807f96f071745943ea8768f7134231a05e55746989e3cbb996eb1.json b/rust/.sqlx/query-44196efd5f9807f96f071745943ea8768f7134231a05e55746989e3cbb996eb1.json
new file mode 100644
index 00000000..b766e16e
--- /dev/null
+++ b/rust/.sqlx/query-44196efd5f9807f96f071745943ea8768f7134231a05e55746989e3cbb996eb1.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO group_histories(\n group_history_id, \n group_id, \n affected_contact_id, \n new_group_name, \n new_delete_messages_after_milliseconds, \n type\n ) VALUES (?, ?, ?, ?, ?, ?)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 6
+ },
+ "nullable": []
+ },
+ "hash": "44196efd5f9807f96f071745943ea8768f7134231a05e55746989e3cbb996eb1"
+}
diff --git a/rust/.sqlx/query-46dcd58d4b0682cf3735addcfa526215a52a5a48969c7b5ab88c0fc1b7609201.json b/rust/.sqlx/query-46dcd58d4b0682cf3735addcfa526215a52a5a48969c7b5ab88c0fc1b7609201.json
new file mode 100644
index 00000000..d1225f01
--- /dev/null
+++ b/rust/.sqlx/query-46dcd58d4b0682cf3735addcfa526215a52a5a48969c7b5ab88c0fc1b7609201.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE messages\n SET is_deleted_from_sender = 1, content = NULL, media_id = NULL, modified_at = ?\n WHERE message_id = ? AND sender_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "46dcd58d4b0682cf3735addcfa526215a52a5a48969c7b5ab88c0fc1b7609201"
+}
diff --git a/rust/.sqlx/query-47802f3954f7e8a17c7cb78cbaf72e68a2191c2a35eb3bb8f045765689d6520e.json b/rust/.sqlx/query-47802f3954f7e8a17c7cb78cbaf72e68a2191c2a35eb3bb8f045765689d6520e.json
new file mode 100644
index 00000000..0a769c10
--- /dev/null
+++ b/rust/.sqlx/query-47802f3954f7e8a17c7cb78cbaf72e68a2191c2a35eb3bb8f045765689d6520e.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO contacts(user_id, username, signal_version, accepted, requested, deleted_by_user, blocked)\n VALUES (?, COALESCE(?, '[Unknown]'), COALESCE(?, 'v2'), COALESCE(?, 0), COALESCE(?, 0), COALESCE(?, 0), COALESCE(?, 0))\n ON CONFLICT(user_id) DO UPDATE SET\n username = COALESCE(?, contacts.username),\n signal_version = COALESCE(?, contacts.signal_version),\n accepted = COALESCE(?, contacts.accepted),\n requested = COALESCE(?, contacts.requested),\n deleted_by_user = COALESCE(?, contacts.deleted_by_user),\n blocked = COALESCE(?, contacts.blocked)\n WHERE ? = 0 OR contacts.requested = 0\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 14
+ },
+ "nullable": []
+ },
+ "hash": "47802f3954f7e8a17c7cb78cbaf72e68a2191c2a35eb3bb8f045765689d6520e"
+}
diff --git a/rust/.sqlx/query-49a771af89168b9e3d09e2b0cbe49eb2a51189212e1cdb99fb8211bd614c9558.json b/rust/.sqlx/query-49a771af89168b9e3d09e2b0cbe49eb2a51189212e1cdb99fb8211bd614c9558.json
deleted file mode 100644
index 53651e0f..00000000
--- a/rust/.sqlx/query-49a771af89168b9e3d09e2b0cbe49eb2a51189212e1cdb99fb8211bd614c9558.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE contacts\n SET requested = 0, deleted_by_user = 0\n WHERE user_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 1
- },
- "nullable": []
- },
- "hash": "49a771af89168b9e3d09e2b0cbe49eb2a51189212e1cdb99fb8211bd614c9558"
-}
diff --git a/rust/.sqlx/query-4ad8ea916d6113c45fef05150f2e619e06ac351ac0b7d0a9b340835ac7109fdf.json b/rust/.sqlx/query-4ad8ea916d6113c45fef05150f2e619e06ac351ac0b7d0a9b340835ac7109fdf.json
new file mode 100644
index 00000000..c41836fe
--- /dev/null
+++ b/rust/.sqlx/query-4ad8ea916d6113c45fef05150f2e619e06ac351ac0b7d0a9b340835ac7109fdf.json
@@ -0,0 +1,28 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(*) AS \"count!: i64\", MAX(last_retry) AS last_retry\n FROM receipts WHERE contact_id = ? AND message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "count!: i64",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ },
+ {
+ "name": "last_retry",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ false,
+ true
+ ]
+ },
+ "hash": "4ad8ea916d6113c45fef05150f2e619e06ac351ac0b7d0a9b340835ac7109fdf"
+}
diff --git a/rust/.sqlx/query-4b8683059700bf2e6ad30130891cc5ac8759171c57fae2271a493e85c2ee4a2e.json b/rust/.sqlx/query-4b8683059700bf2e6ad30130891cc5ac8759171c57fae2271a493e85c2ee4a2e.json
new file mode 100644
index 00000000..6b0a4d0d
--- /dev/null
+++ b/rust/.sqlx/query-4b8683059700bf2e6ad30130891cc5ac8759171c57fae2271a493e85c2ee4a2e.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT announced_user_id, announced_public_key\n FROM user_discovery_announced_users WHERE username IS NULL",
+ "describe": {
+ "columns": [
+ {
+ "name": "announced_user_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "user_discovery_announced_users",
+ "name": "announced_user_id"
+ }
+ }
+ },
+ {
+ "name": "announced_public_key",
+ "ordinal": 1,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "user_discovery_announced_users",
+ "name": "announced_public_key"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false,
+ false
+ ]
+ },
+ "hash": "4b8683059700bf2e6ad30130891cc5ac8759171c57fae2271a493e85c2ee4a2e"
+}
diff --git a/rust/.sqlx/query-4cceb9755b12fe18a8462b8e38ada4a606a8c973b2dbb7a70fb25bd0938f9c24.json b/rust/.sqlx/query-4cceb9755b12fe18a8462b8e38ada4a606a8c973b2dbb7a70fb25bd0938f9c24.json
new file mode 100644
index 00000000..c41102fc
--- /dev/null
+++ b/rust/.sqlx/query-4cceb9755b12fe18a8462b8e38ada4a606a8c973b2dbb7a70fb25bd0938f9c24.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT delete_messages_after_milliseconds FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "delete_messages_after_milliseconds",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "delete_messages_after_milliseconds"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "4cceb9755b12fe18a8462b8e38ada4a606a8c973b2dbb7a70fb25bd0938f9c24"
+}
diff --git a/rust/.sqlx/query-4cf56a4c69c3f809333c83aae082980edd05095bf43bc00b481d613ad250724c.json b/rust/.sqlx/query-4cf56a4c69c3f809333c83aae082980edd05095bf43bc00b481d613ad250724c.json
new file mode 100644
index 00000000..2ecfa264
--- /dev/null
+++ b/rust/.sqlx/query-4cf56a4c69c3f809333c83aae082980edd05095bf43bc00b481d613ad250724c.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT MAX(created_at, ?) AS \"action_at!: i64\" FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "action_at!: i64",
+ "ordinal": 0,
+ "type_info": "Null",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ null
+ ]
+ },
+ "hash": "4cf56a4c69c3f809333c83aae082980edd05095bf43bc00b481d613ad250724c"
+}
diff --git a/rust/.sqlx/query-5357d7c57bd2d7a7d2c52e4dd41e90bdea08f915d736745592ddd375171787f8.json b/rust/.sqlx/query-5357d7c57bd2d7a7d2c52e4dd41e90bdea08f915d736745592ddd375171787f8.json
deleted file mode 100644
index f413e567..00000000
--- a/rust/.sqlx/query-5357d7c57bd2d7a7d2c52e4dd41e90bdea08f915d736745592ddd375171787f8.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE messages\n SET is_deleted_from_sender = 1, modified_at = ?\n WHERE message_id = ? AND sender_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 3
- },
- "nullable": []
- },
- "hash": "5357d7c57bd2d7a7d2c52e4dd41e90bdea08f915d736745592ddd375171787f8"
-}
diff --git a/rust/.sqlx/query-53e9edf1571d1f0fe88384aa3423846fd4a4d9a202c5d7e706c54c7a91df215d.json b/rust/.sqlx/query-53e9edf1571d1f0fe88384aa3423846fd4a4d9a202c5d7e706c54c7a91df215d.json
new file mode 100644
index 00000000..1c06c82b
--- /dev/null
+++ b/rust/.sqlx/query-53e9edf1571d1f0fe88384aa3423846fd4a4d9a202c5d7e706c54c7a91df215d.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO message_actions(message_id, contact_id, type, action_at)\n SELECT messages.message_id, group_members.contact_id, 'ackByServerAt', ?\n FROM messages\n JOIN group_members ON group_members.group_id = messages.group_id\n WHERE messages.media_id = ?\n AND (group_members.member_state IS NULL OR group_members.member_state != 'leftGroup')\n ON CONFLICT(message_id, contact_id, type)\n DO UPDATE SET action_at = excluded.action_at\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "53e9edf1571d1f0fe88384aa3423846fd4a4d9a202c5d7e706c54c7a91df215d"
+}
diff --git a/rust/.sqlx/query-58cd5f927d873ff3325f391cd8c6b9ca9e68bb6cd423dce99be407402ced523e.json b/rust/.sqlx/query-58cd5f927d873ff3325f391cd8c6b9ca9e68bb6cd423dce99be407402ced523e.json
new file mode 100644
index 00000000..ba95aa4a
--- /dev/null
+++ b/rust/.sqlx/query-58cd5f927d873ff3325f391cd8c6b9ca9e68bb6cd423dce99be407402ced523e.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO receipts(receipt_id, contact_id, message, contact_will_sends_receipt, wake_receiver)\n VALUES (?, ?, ?, ?, ?)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 5
+ },
+ "nullable": []
+ },
+ "hash": "58cd5f927d873ff3325f391cd8c6b9ca9e68bb6cd423dce99be407402ced523e"
+}
diff --git a/rust/.sqlx/query-5a183a98692dd2aa25f61799cfbcf794e507a69be0524624b4ba75d69fc9fa1a.json b/rust/.sqlx/query-5a183a98692dd2aa25f61799cfbcf794e507a69be0524624b4ba75d69fc9fa1a.json
new file mode 100644
index 00000000..1f43ab4d
--- /dev/null
+++ b/rust/.sqlx/query-5a183a98692dd2aa25f61799cfbcf794e507a69be0524624b4ba75d69fc9fa1a.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT draft_message FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "draft_message",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "draft_message"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "5a183a98692dd2aa25f61799cfbcf794e507a69be0524624b4ba75d69fc9fa1a"
+}
diff --git a/rust/.sqlx/query-5a7ba59111d6353f7a18d3f8eb8fa1ae3a491f080a4735361e9aaa41ccaa7d51.json b/rust/.sqlx/query-5a7ba59111d6353f7a18d3f8eb8fa1ae3a491f080a4735361e9aaa41ccaa7d51.json
deleted file mode 100644
index f91fa816..00000000
--- a/rust/.sqlx/query-5a7ba59111d6353f7a18d3f8eb8fa1ae3a491f080a4735361e9aaa41ccaa7d51.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE user_discovery_own_promotions\n SET promotion = X''\n WHERE contact_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 1
- },
- "nullable": []
- },
- "hash": "5a7ba59111d6353f7a18d3f8eb8fa1ae3a491f080a4735361e9aaa41ccaa7d51"
-}
diff --git a/rust/.sqlx/query-5b1b9e6003bab16e21e1c38f5115c854c13749275db28bb96970e2056cf851c1.json b/rust/.sqlx/query-5b1b9e6003bab16e21e1c38f5115c854c13749275db28bb96970e2056cf851c1.json
new file mode 100644
index 00000000..f7c0ce3f
--- /dev/null
+++ b/rust/.sqlx/query-5b1b9e6003bab16e21e1c38f5115c854c13749275db28bb96970e2056cf851c1.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE notification_outbox SET cleared_at = ? WHERE conversation_id = ? AND cleared_at IS NULL",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "5b1b9e6003bab16e21e1c38f5115c854c13749275db28bb96970e2056cf851c1"
+}
diff --git a/rust/.sqlx/query-07d19c693f05b07d6d3c29ff7c77571d8dd6b4618434e36f2d9d97a9178ddb3b.json b/rust/.sqlx/query-5b3a65331dc9b6528819f6fddb248917c4419b0d68b76e662eb3f76ed2975b14.json
similarity index 50%
rename from rust/.sqlx/query-07d19c693f05b07d6d3c29ff7c77571d8dd6b4618434e36f2d9d97a9178ddb3b.json
rename to rust/.sqlx/query-5b3a65331dc9b6528819f6fddb248917c4419b0d68b76e662eb3f76ed2975b14.json
index 97a6f7ef..37c6b4b8 100644
--- a/rust/.sqlx/query-07d19c693f05b07d6d3c29ff7c77571d8dd6b4618434e36f2d9d97a9178ddb3b.json
+++ b/rust/.sqlx/query-5b3a65331dc9b6528819f6fddb248917c4419b0d68b76e662eb3f76ed2975b14.json
@@ -1,10 +1,10 @@
{
"db_name": "SQLite",
- "query": "\n SELECT my_group_private_key IS NOT NULL\n FROM groups\n WHERE group_id = ?\n ",
+ "query": "SELECT COUNT(*) FROM receipts WHERE contact_id = ?",
"describe": {
"columns": [
{
- "name": "my_group_private_key IS NOT NULL",
+ "name": "COUNT(*)",
"ordinal": 0,
"type_info": "Integer",
"origin": "Expression"
@@ -17,5 +17,5 @@
false
]
},
- "hash": "07d19c693f05b07d6d3c29ff7c77571d8dd6b4618434e36f2d9d97a9178ddb3b"
+ "hash": "5b3a65331dc9b6528819f6fddb248917c4419b0d68b76e662eb3f76ed2975b14"
}
diff --git a/rust/.sqlx/query-5b848eba9965a386b9189ff911015c9652305c82281efd37491139b814a4562a.json b/rust/.sqlx/query-5b848eba9965a386b9189ff911015c9652305c82281efd37491139b814a4562a.json
deleted file mode 100644
index aeaec2f6..00000000
--- a/rust/.sqlx/query-5b848eba9965a386b9189ff911015c9652305c82281efd37491139b814a4562a.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE groups\n SET also_best_friend = ?,\n flame_counter = MAX(flame_counter, ?),\n max_flame_counter = MAX(max_flame_counter, ?)\n WHERE group_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 4
- },
- "nullable": []
- },
- "hash": "5b848eba9965a386b9189ff911015c9652305c82281efd37491139b814a4562a"
-}
diff --git a/rust/.sqlx/query-5c21ee90d244d4859b3b9544f182dd071b91356acbf297ced95ebc496ff03091.json b/rust/.sqlx/query-5c21ee90d244d4859b3b9544f182dd071b91356acbf297ced95ebc496ff03091.json
deleted file mode 100644
index b77bdb16..00000000
--- a/rust/.sqlx/query-5c21ee90d244d4859b3b9544f182dd071b91356acbf297ced95ebc496ff03091.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE contacts\n SET accepted = 0, requested = 1\n WHERE user_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 1
- },
- "nullable": []
- },
- "hash": "5c21ee90d244d4859b3b9544f182dd071b91356acbf297ced95ebc496ff03091"
-}
diff --git a/rust/.sqlx/query-5cdf6bbec2b16b9917ee87af7cd4ca712af5966ebc9a93978317d708c2f960fd.json b/rust/.sqlx/query-5cdf6bbec2b16b9917ee87af7cd4ca712af5966ebc9a93978317d708c2f960fd.json
new file mode 100644
index 00000000..b438e285
--- /dev/null
+++ b/rust/.sqlx/query-5cdf6bbec2b16b9917ee87af7cd4ca712af5966ebc9a93978317d708c2f960fd.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM media_files WHERE media_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "5cdf6bbec2b16b9917ee87af7cd4ca712af5966ebc9a93978317d708c2f960fd"
+}
diff --git a/rust/.sqlx/query-5e79f50002566a12094f2aed7771a19c98dfab500af103f25f3c188ee53006c6.json b/rust/.sqlx/query-5e79f50002566a12094f2aed7771a19c98dfab500af103f25f3c188ee53006c6.json
new file mode 100644
index 00000000..bb96adf3
--- /dev/null
+++ b/rust/.sqlx/query-5e79f50002566a12094f2aed7771a19c98dfab500af103f25f3c188ee53006c6.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(*) AS \"count: i64\" FROM notification_outbox WHERE cleared_at IS NULL",
+ "describe": {
+ "columns": [
+ {
+ "name": "count: i64",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "5e79f50002566a12094f2aed7771a19c98dfab500af103f25f3c188ee53006c6"
+}
diff --git a/rust/.sqlx/query-9f78cc93c1680c63c1a258ca2ce1de0ea43bf3a57382b0505828d34c562f96a6.json b/rust/.sqlx/query-60db27444be839288588e655829a7f940f23a9a86cb8b1803a74ff4cf94c00f1.json
similarity index 75%
rename from rust/.sqlx/query-9f78cc93c1680c63c1a258ca2ce1de0ea43bf3a57382b0505828d34c562f96a6.json
rename to rust/.sqlx/query-60db27444be839288588e655829a7f940f23a9a86cb8b1803a74ff4cf94c00f1.json
index 711cb24f..2fac717e 100644
--- a/rust/.sqlx/query-9f78cc93c1680c63c1a258ca2ce1de0ea43bf3a57382b0505828d34c562f96a6.json
+++ b/rust/.sqlx/query-60db27444be839288588e655829a7f940f23a9a86cb8b1803a74ff4cf94c00f1.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT\n announced_user_id,\n announced_public_key,\n public_id\n FROM user_discovery_announced_users\n WHERE public_id = ?\n ",
+ "query": "\n SELECT announced_user_id, announced_public_key, public_id\n FROM user_discovery_announced_users\n WHERE public_id = ?\n ",
"describe": {
"columns": [
{
@@ -46,5 +46,5 @@
false
]
},
- "hash": "9f78cc93c1680c63c1a258ca2ce1de0ea43bf3a57382b0505828d34c562f96a6"
+ "hash": "60db27444be839288588e655829a7f940f23a9a86cb8b1803a74ff4cf94c00f1"
}
diff --git a/rust/.sqlx/query-61c2483b50f9f5c8a586eaa2c2381b06898954f5c898abf280b9ab02d96ff457.json b/rust/.sqlx/query-61c2483b50f9f5c8a586eaa2c2381b06898954f5c898abf280b9ab02d96ff457.json
deleted file mode 100644
index c10f1ad5..00000000
--- a/rust/.sqlx/query-61c2483b50f9f5c8a586eaa2c2381b06898954f5c898abf280b9ab02d96ff457.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO messages(group_id, message_id, sender_id, type)\n VALUES('group', 'message', 7, 'text')\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 0
- },
- "nullable": []
- },
- "hash": "61c2483b50f9f5c8a586eaa2c2381b06898954f5c898abf280b9ab02d96ff457"
-}
diff --git a/rust/.sqlx/query-629c9b14b6e65f3209752e66b4f7709d252feae82dd009ce628274ed9b9c680a.json b/rust/.sqlx/query-629c9b14b6e65f3209752e66b4f7709d252feae82dd009ce628274ed9b9c680a.json
deleted file mode 100644
index 2dd4cc25..00000000
--- a/rust/.sqlx/query-629c9b14b6e65f3209752e66b4f7709d252feae82dd009ce628274ed9b9c680a.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE user_discovery_shares\n SET contact_id = ?\n WHERE share_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "629c9b14b6e65f3209752e66b4f7709d252feae82dd009ce628274ed9b9c680a"
-}
diff --git a/rust/.sqlx/query-638a0ec306d93a0c9c57abcd61437c0bec21dfa994a5398d7e87bd4d99b6fb92.json b/rust/.sqlx/query-638a0ec306d93a0c9c57abcd61437c0bec21dfa994a5398d7e87bd4d99b6fb92.json
new file mode 100644
index 00000000..360317bd
--- /dev/null
+++ b/rust/.sqlx/query-638a0ec306d93a0c9c57abcd61437c0bec21dfa994a5398d7e87bd4d99b6fb92.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET username = ? WHERE user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "638a0ec306d93a0c9c57abcd61437c0bec21dfa994a5398d7e87bd4d99b6fb92"
+}
diff --git a/rust/.sqlx/query-65c975e50cc9edb95d66cac2698429f03d2fdc1d0a17e9155267491f68af3d1f.json b/rust/.sqlx/query-65c975e50cc9edb95d66cac2698429f03d2fdc1d0a17e9155267491f68af3d1f.json
new file mode 100644
index 00000000..9e562dbf
--- /dev/null
+++ b/rust/.sqlx/query-65c975e50cc9edb95d66cac2698429f03d2fdc1d0a17e9155267491f68af3d1f.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "\n SELECT my_group_private_key\n FROM groups\n WHERE group_id = ?\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "my_group_private_key",
+ "ordinal": 0,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "my_group_private_key"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "65c975e50cc9edb95d66cac2698429f03d2fdc1d0a17e9155267491f68af3d1f"
+}
diff --git a/rust/.sqlx/query-70f9d68d251f1c354c61584a84f509682e341595e0d98cb880c50ec80f11f02a.json b/rust/.sqlx/query-6668c78b62a4d359c1027219abe352fec2832bbeaa721cf29bf8961868d867dd.json
similarity index 65%
rename from rust/.sqlx/query-70f9d68d251f1c354c61584a84f509682e341595e0d98cb880c50ec80f11f02a.json
rename to rust/.sqlx/query-6668c78b62a4d359c1027219abe352fec2832bbeaa721cf29bf8961868d867dd.json
index 29b00d56..1fece209 100644
--- a/rust/.sqlx/query-70f9d68d251f1c354c61584a84f509682e341595e0d98cb880c50ec80f11f02a.json
+++ b/rust/.sqlx/query-6668c78b62a4d359c1027219abe352fec2832bbeaa721cf29bf8961868d867dd.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT user_discovery_version\n FROM contacts\n WHERE user_id = ?\n ",
+ "query": "SELECT user_discovery_version FROM contacts WHERE user_id = ?",
"describe": {
"columns": [
{
@@ -22,5 +22,5 @@
true
]
},
- "hash": "70f9d68d251f1c354c61584a84f509682e341595e0d98cb880c50ec80f11f02a"
+ "hash": "6668c78b62a4d359c1027219abe352fec2832bbeaa721cf29bf8961868d867dd"
}
diff --git a/rust/.sqlx/query-668764467495cec13ad814f2d5e3f272b0fa1476fe24f22d40eb721baaf0f2b7.json b/rust/.sqlx/query-668764467495cec13ad814f2d5e3f272b0fa1476fe24f22d40eb721baaf0f2b7.json
new file mode 100644
index 00000000..fd1f3edb
--- /dev/null
+++ b/rust/.sqlx/query-668764467495cec13ad814f2d5e3f272b0fa1476fe24f22d40eb721baaf0f2b7.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE groups SET draft_message = NULL WHERE group_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "668764467495cec13ad814f2d5e3f272b0fa1476fe24f22d40eb721baaf0f2b7"
+}
diff --git a/rust/.sqlx/query-6a2ad15584f9fb4a07ff035e3d4277932b702e63a1887b9c873a62d058ce6a69.json b/rust/.sqlx/query-6a2ad15584f9fb4a07ff035e3d4277932b702e63a1887b9c873a62d058ce6a69.json
new file mode 100644
index 00000000..1e93f223
--- /dev/null
+++ b/rust/.sqlx/query-6a2ad15584f9fb4a07ff035e3d4277932b702e63a1887b9c873a62d058ce6a69.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO messages(group_id, message_id, type, media_id, created_at) VALUES (?, ?, 'media', ?, CAST(strftime('%s','now') AS INTEGER))",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "6a2ad15584f9fb4a07ff035e3d4277932b702e63a1887b9c873a62d058ce6a69"
+}
diff --git a/rust/.sqlx/query-6a54a91ead7d088a7d47e2bf6e869ac7b4c2d1b7f5870ab5ab0e160fb99fae83.json b/rust/.sqlx/query-6a54a91ead7d088a7d47e2bf6e869ac7b4c2d1b7f5870ab5ab0e160fb99fae83.json
new file mode 100644
index 00000000..5de3fb10
--- /dev/null
+++ b/rust/.sqlx/query-6a54a91ead7d088a7d47e2bf6e869ac7b4c2d1b7f5870ab5ab0e160fb99fae83.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT notification_id FROM notification_outbox WHERE conversation_id = ? AND cleared_at IS NULL",
+ "describe": {
+ "columns": [
+ {
+ "name": "notification_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "notification_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "6a54a91ead7d088a7d47e2bf6e869ac7b4c2d1b7f5870ab5ab0e160fb99fae83"
+}
diff --git a/rust/.sqlx/query-6c5f0fd707422f9e5808a3b7efeb18d4b3c27eb605637b932bbd2d4fbf23e8a9.json b/rust/.sqlx/query-6c5f0fd707422f9e5808a3b7efeb18d4b3c27eb605637b932bbd2d4fbf23e8a9.json
new file mode 100644
index 00000000..7ea8a1d4
--- /dev/null
+++ b/rust/.sqlx/query-6c5f0fd707422f9e5808a3b7efeb18d4b3c27eb605637b932bbd2d4fbf23e8a9.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO message_actions(message_id, contact_id, type)\n VALUES (?, ?, 'ackByServerAt')\n ON CONFLICT(message_id, contact_id, type)\n DO UPDATE SET action_at = CAST(strftime('%s', 'now') AS INTEGER)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "6c5f0fd707422f9e5808a3b7efeb18d4b3c27eb605637b932bbd2d4fbf23e8a9"
+}
diff --git a/rust/.sqlx/query-6ceb2e7b39ba54d335210bbc71c56e68ac09881f86189e833ab60ec34778b2ae.json b/rust/.sqlx/query-6ceb2e7b39ba54d335210bbc71c56e68ac09881f86189e833ab60ec34778b2ae.json
new file mode 100644
index 00000000..de6707f7
--- /dev/null
+++ b/rust/.sqlx/query-6ceb2e7b39ba54d335210bbc71c56e68ac09881f86189e833ab60ec34778b2ae.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM signal_identities WHERE name = ?)",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM signal_identities WHERE name = ?)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "6ceb2e7b39ba54d335210bbc71c56e68ac09881f86189e833ab60ec34778b2ae"
+}
diff --git a/rust/.sqlx/query-6d2c616fc02e1fdc3686b34de0440936a9cb54e5921b5b17bfe17c021e12ca23.json b/rust/.sqlx/query-6d2c616fc02e1fdc3686b34de0440936a9cb54e5921b5b17bfe17c021e12ca23.json
new file mode 100644
index 00000000..195d8ec5
--- /dev/null
+++ b/rust/.sqlx/query-6d2c616fc02e1fdc3686b34de0440936a9cb54e5921b5b17bfe17c021e12ca23.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO signal_identities(name, identity_key, timestamp)\n VALUES (?, ?, CAST(strftime('%s', 'now') AS INTEGER))\n ON CONFLICT(name) DO NOTHING\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "6d2c616fc02e1fdc3686b34de0440936a9cb54e5921b5b17bfe17c021e12ca23"
+}
diff --git a/rust/.sqlx/query-6d6a5fb10742309ab6e4242179ef86b64ac9f751f1c6d647d4ecfa5ecac5d332.json b/rust/.sqlx/query-6d6a5fb10742309ab6e4242179ef86b64ac9f751f1c6d647d4ecfa5ecac5d332.json
new file mode 100644
index 00000000..98aec940
--- /dev/null
+++ b/rust/.sqlx/query-6d6a5fb10742309ab6e4242179ef86b64ac9f751f1c6d647d4ecfa5ecac5d332.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "\n SELECT group_id, contact_id \n FROM group_members \n WHERE group_public_key IS NULL \n AND last_message >= CAST(strftime('%s','now','-2 days') AS INTEGER)\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "group_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "group_members",
+ "name": "group_id"
+ }
+ }
+ },
+ {
+ "name": "contact_id",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "group_members",
+ "name": "contact_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false,
+ false
+ ]
+ },
+ "hash": "6d6a5fb10742309ab6e4242179ef86b64ac9f751f1c6d647d4ecfa5ecac5d332"
+}
diff --git a/rust/.sqlx/query-6e69b233b4bd2b2a5aedb8057780a929eb6f3ac1dbe2b4c753ab0ec760a628fa.json b/rust/.sqlx/query-6e69b233b4bd2b2a5aedb8057780a929eb6f3ac1dbe2b4c753ab0ec760a628fa.json
new file mode 100644
index 00000000..0977b5a7
--- /dev/null
+++ b/rust/.sqlx/query-6e69b233b4bd2b2a5aedb8057780a929eb6f3ac1dbe2b4c753ab0ec760a628fa.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE media_files SET download_state = 'pending'\n WHERE media_id = ? AND download_state = 'downloading'",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "6e69b233b4bd2b2a5aedb8057780a929eb6f3ac1dbe2b4c753ab0ec760a628fa"
+}
diff --git a/rust/.sqlx/query-6e7d6401f94c0a597c4a62db858cc409d46c19d11f9d4419fc95caaf38968471.json b/rust/.sqlx/query-6e7d6401f94c0a597c4a62db858cc409d46c19d11f9d4419fc95caaf38968471.json
new file mode 100644
index 00000000..fb752c5f
--- /dev/null
+++ b/rust/.sqlx/query-6e7d6401f94c0a597c4a62db858cc409d46c19d11f9d4419fc95caaf38968471.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT flame_counter FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "flame_counter",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "flame_counter"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "6e7d6401f94c0a597c4a62db858cc409d46c19d11f9d4419fc95caaf38968471"
+}
diff --git a/rust/.sqlx/query-713b45ba555a737f059569886ba38eedcd0664aa6c8a6d2190802b7a85e77371.json b/rust/.sqlx/query-713b45ba555a737f059569886ba38eedcd0664aa6c8a6d2190802b7a85e77371.json
deleted file mode 100644
index 7a8dfc58..00000000
--- a/rust/.sqlx/query-713b45ba555a737f059569886ba38eedcd0664aa6c8a6d2190802b7a85e77371.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n SELECT EXISTS(\n SELECT 1 FROM contacts WHERE user_id = ?\n )\n ",
- "describe": {
- "columns": [
- {
- "name": "EXISTS(\n SELECT 1 FROM contacts WHERE user_id = ?\n )",
- "ordinal": 0,
- "type_info": "Integer",
- "origin": "Expression"
- }
- ],
- "parameters": {
- "Right": 1
- },
- "nullable": [
- false
- ]
- },
- "hash": "713b45ba555a737f059569886ba38eedcd0664aa6c8a6d2190802b7a85e77371"
-}
diff --git a/rust/.sqlx/query-718877ef8c91c122ab283500ddf4594623234a5b8eca43f6b89baa0b17ff8df0.json b/rust/.sqlx/query-718877ef8c91c122ab283500ddf4594623234a5b8eca43f6b89baa0b17ff8df0.json
new file mode 100644
index 00000000..943f3540
--- /dev/null
+++ b/rust/.sqlx/query-718877ef8c91c122ab283500ddf4594623234a5b8eca43f6b89baa0b17ff8df0.json
@@ -0,0 +1,50 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT m.message_id, m.sender_id, c.account_deleted\n FROM messages m\n LEFT JOIN contacts c ON c.user_id = m.sender_id\n WHERE m.media_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "message_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "message_id"
+ }
+ }
+ },
+ {
+ "name": "sender_id",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "sender_id"
+ }
+ }
+ },
+ {
+ "name": "account_deleted",
+ "ordinal": 2,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "account_deleted"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ true,
+ true
+ ]
+ },
+ "hash": "718877ef8c91c122ab283500ddf4594623234a5b8eca43f6b89baa0b17ff8df0"
+}
diff --git a/rust/.sqlx/query-732496b1ad12c49c3aca6925d45abc4c801838e606d9bfd3f728f4e1dd91dd3a.json b/rust/.sqlx/query-732496b1ad12c49c3aca6925d45abc4c801838e606d9bfd3f728f4e1dd91dd3a.json
new file mode 100644
index 00000000..8155b131
--- /dev/null
+++ b/rust/.sqlx/query-732496b1ad12c49c3aca6925d45abc4c801838e606d9bfd3f728f4e1dd91dd3a.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE receipts SET message_id = ?, will_be_retried_by_media_upload = ?,\n retry_count = ?, last_retry = ? WHERE receipt_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 5
+ },
+ "nullable": []
+ },
+ "hash": "732496b1ad12c49c3aca6925d45abc4c801838e606d9bfd3f728f4e1dd91dd3a"
+}
diff --git a/rust/.sqlx/query-7498999dcb013e18ec840820caeb996779d7aa4d74b69c8da97f0c429a41a3aa.json b/rust/.sqlx/query-7498999dcb013e18ec840820caeb996779d7aa4d74b69c8da97f0c429a41a3aa.json
deleted file mode 100644
index 14ad027f..00000000
--- a/rust/.sqlx/query-7498999dcb013e18ec840820caeb996779d7aa4d74b69c8da97f0c429a41a3aa.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE contacts\n SET requested = 0, accepted = 1, deleted_by_user = 0\n WHERE user_id = ? AND requested = 0\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 1
- },
- "nullable": []
- },
- "hash": "7498999dcb013e18ec840820caeb996779d7aa4d74b69c8da97f0c429a41a3aa"
-}
diff --git a/rust/.sqlx/query-7501f88f74cef11e63a654627e86ba29da7cd28fed9450c7c5754509cb855dfa.json b/rust/.sqlx/query-7501f88f74cef11e63a654627e86ba29da7cd28fed9450c7c5754509cb855dfa.json
new file mode 100644
index 00000000..eb871cf6
--- /dev/null
+++ b/rust/.sqlx/query-7501f88f74cef11e63a654627e86ba29da7cd28fed9450c7c5754509cb855dfa.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET user_discovery_version = ? WHERE user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "7501f88f74cef11e63a654627e86ba29da7cd28fed9450c7c5754509cb855dfa"
+}
diff --git a/rust/.sqlx/query-77b10c847e251dff552808d110d6e843d66e372742b4d225263b4c73406639eb.json b/rust/.sqlx/query-77b10c847e251dff552808d110d6e843d66e372742b4d225263b4c73406639eb.json
new file mode 100644
index 00000000..1d45b0bc
--- /dev/null
+++ b/rust/.sqlx/query-77b10c847e251dff552808d110d6e843d66e372742b4d225263b4c73406639eb.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE groups SET last_flame_sync = ? WHERE group_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "77b10c847e251dff552808d110d6e843d66e372742b4d225263b4c73406639eb"
+}
diff --git a/rust/.sqlx/query-bd4750be71cfd64ab00d5a84d28f9df5cca1e61750a186328c65d384cfcefa15.json b/rust/.sqlx/query-78ad566837d0a3263b1daa3149971d1b2f75efcf7c03ae7ee452d7b9727d4ede.json
similarity index 72%
rename from rust/.sqlx/query-bd4750be71cfd64ab00d5a84d28f9df5cca1e61750a186328c65d384cfcefa15.json
rename to rust/.sqlx/query-78ad566837d0a3263b1daa3149971d1b2f75efcf7c03ae7ee452d7b9727d4ede.json
index 696e9659..481eb50e 100644
--- a/rust/.sqlx/query-bd4750be71cfd64ab00d5a84d28f9df5cca1e61750a186328c65d384cfcefa15.json
+++ b/rust/.sqlx/query-78ad566837d0a3263b1daa3149971d1b2f75efcf7c03ae7ee452d7b9727d4ede.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT share_id, share\n FROM user_discovery_shares\n WHERE contact_id IS NULL\n LIMIT 1\n ",
+ "query": "SELECT share_id, share FROM user_discovery_shares WHERE contact_id IS NULL LIMIT 1",
"describe": {
"columns": [
{
@@ -34,5 +34,5 @@
false
]
},
- "hash": "bd4750be71cfd64ab00d5a84d28f9df5cca1e61750a186328c65d384cfcefa15"
+ "hash": "78ad566837d0a3263b1daa3149971d1b2f75efcf7c03ae7ee452d7b9727d4ede"
}
diff --git a/rust/.sqlx/query-794398164165a4a07387a201ce61b9b81b2712517abd4ae77565d7eac22bda41.json b/rust/.sqlx/query-794398164165a4a07387a201ce61b9b81b2712517abd4ae77565d7eac22bda41.json
new file mode 100644
index 00000000..5387fa45
--- /dev/null
+++ b/rust/.sqlx/query-794398164165a4a07387a201ce61b9b81b2712517abd4ae77565d7eac22bda41.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO messages(\n group_id,\n message_id,\n sender_id,\n type,\n content,\n media_id,\n additional_message_data,\n quotes_message_id,\n created_at,\n ack_by_server\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(message_id) DO NOTHING\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 10
+ },
+ "nullable": []
+ },
+ "hash": "794398164165a4a07387a201ce61b9b81b2712517abd4ae77565d7eac22bda41"
+}
diff --git a/rust/.sqlx/query-794e14789178809605447e9100342e824c9086817c1074503d8bf71e60828913.json b/rust/.sqlx/query-794e14789178809605447e9100342e824c9086817c1074503d8bf71e60828913.json
new file mode 100644
index 00000000..8df54790
--- /dev/null
+++ b/rust/.sqlx/query-794e14789178809605447e9100342e824c9086817c1074503d8bf71e60828913.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE groups \n SET group_name = ?, \n delete_messages_after_milliseconds = COALESCE(?, delete_messages_after_milliseconds), \n is_group_admin = ?, \n joined_group = ?, \n left_group = ?, \n state_version_id = ? \n WHERE group_id = ?\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 7
+ },
+ "nullable": []
+ },
+ "hash": "794e14789178809605447e9100342e824c9086817c1074503d8bf71e60828913"
+}
diff --git a/rust/.sqlx/query-79a57c847e685ec171bba8db07dd9c76794b12d8cb04bf5f351636f8cd1409f8.json b/rust/.sqlx/query-79a57c847e685ec171bba8db07dd9c76794b12d8cb04bf5f351636f8cd1409f8.json
new file mode 100644
index 00000000..111a9ed4
--- /dev/null
+++ b/rust/.sqlx/query-79a57c847e685ec171bba8db07dd9c76794b12d8cb04bf5f351636f8cd1409f8.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO message_actions(message_id, contact_id, type, action_at)\n VALUES (?, ?, 'openedAt', ?)\n ON CONFLICT(message_id, contact_id, type)\n DO UPDATE SET action_at = excluded.action_at",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "79a57c847e685ec171bba8db07dd9c76794b12d8cb04bf5f351636f8cd1409f8"
+}
diff --git a/rust/.sqlx/query-7ba6ad25a53db6d9ee725ad8dd06a58c119e4f445edf966b97016fb6d598f09a.json b/rust/.sqlx/query-7ba6ad25a53db6d9ee725ad8dd06a58c119e4f445edf966b97016fb6d598f09a.json
new file mode 100644
index 00000000..0703e471
--- /dev/null
+++ b/rust/.sqlx/query-7ba6ad25a53db6d9ee725ad8dd06a58c119e4f445edf966b97016fb6d598f09a.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO messages(group_id, message_id, type, additional_message_data, created_at)\n VALUES (?, ?, ?, ?, ?)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 5
+ },
+ "nullable": []
+ },
+ "hash": "7ba6ad25a53db6d9ee725ad8dd06a58c119e4f445edf966b97016fb6d598f09a"
+}
diff --git a/rust/.sqlx/query-7da111f61862ce2262bb377213c780e8a817a1bd5f53f9493c0f5314df312b44.json b/rust/.sqlx/query-7da111f61862ce2262bb377213c780e8a817a1bd5f53f9493c0f5314df312b44.json
new file mode 100644
index 00000000..ad756273
--- /dev/null
+++ b/rust/.sqlx/query-7da111f61862ce2262bb377213c780e8a817a1bd5f53f9493c0f5314df312b44.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT is_group_admin FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "is_group_admin",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "is_group_admin"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "7da111f61862ce2262bb377213c780e8a817a1bd5f53f9493c0f5314df312b44"
+}
diff --git a/rust/.sqlx/query-7da84cfdaf59649fe3735b81d4805de49530ed63fb35b06ff111743bd809d35d.json b/rust/.sqlx/query-7da84cfdaf59649fe3735b81d4805de49530ed63fb35b06ff111743bd809d35d.json
new file mode 100644
index 00000000..488ba474
--- /dev/null
+++ b/rust/.sqlx/query-7da84cfdaf59649fe3735b81d4805de49530ed63fb35b06ff111743bd809d35d.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET account_deleted = 1 WHERE user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "7da84cfdaf59649fe3735b81d4805de49530ed63fb35b06ff111743bd809d35d"
+}
diff --git a/rust/.sqlx/query-7eb90b44fd4140ce19db1cf4f953910faa6c32fd434dcdfb7ddc2e4d8dc5c97b.json b/rust/.sqlx/query-7eb90b44fd4140ce19db1cf4f953910faa6c32fd434dcdfb7ddc2e4d8dc5c97b.json
new file mode 100644
index 00000000..a137ca1a
--- /dev/null
+++ b/rust/.sqlx/query-7eb90b44fd4140ce19db1cf4f953910faa6c32fd434dcdfb7ddc2e4d8dc5c97b.json
@@ -0,0 +1,50 @@
+{
+ "db_name": "SQLite",
+ "query": "\n SELECT m.sender_id, m.media_id, f.download_state\n FROM messages m\n LEFT JOIN media_files f ON f.media_id = m.media_id\n WHERE message_id = ?\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "sender_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "sender_id"
+ }
+ }
+ },
+ {
+ "name": "media_id",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "media_id"
+ }
+ }
+ },
+ {
+ "name": "download_state",
+ "ordinal": 2,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "download_state"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true,
+ true,
+ true
+ ]
+ },
+ "hash": "7eb90b44fd4140ce19db1cf4f953910faa6c32fd434dcdfb7ddc2e4d8dc5c97b"
+}
diff --git a/rust/.sqlx/query-7ee254f54718b616580ea6ca979679f8ec8065490cf0ffd0a8f44818c1f57167.json b/rust/.sqlx/query-7ee254f54718b616580ea6ca979679f8ec8065490cf0ffd0a8f44818c1f57167.json
new file mode 100644
index 00000000..ae7ac386
--- /dev/null
+++ b/rust/.sqlx/query-7ee254f54718b616580ea6ca979679f8ec8065490cf0ffd0a8f44818c1f57167.json
@@ -0,0 +1,184 @@
+{
+ "db_name": "SQLite",
+ "query": "\n SELECT n.event_id, n.notification_id, n.conversation_id, n.sender_id,\n COALESCE(c.display_name, c.username) AS \"sender_name!: String\",\n c.avatar_svg_compressed, c.sender_profile_counter,\n g.group_name AS conversation_name,\n COALESCE(g.is_direct_chat, 0) AS \"is_direct_chat!: i64\",\n n.message_id, n.kind, n.content, n.created_at,\n target_message.type AS target_message_type,\n target_media.type AS target_media_type\n FROM notification_outbox n\n JOIN contacts c ON c.user_id = n.sender_id\n LEFT JOIN groups g ON g.group_id = n.conversation_id\n LEFT JOIN messages target_message ON target_message.message_id = n.message_id\n LEFT JOIN media_files target_media ON target_media.media_id = target_message.media_id\n WHERE n.delivered_at IS NULL AND n.cleared_at IS NULL\n ORDER BY n.created_at, n.event_id\n LIMIT ?\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "event_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "event_id"
+ }
+ }
+ },
+ {
+ "name": "notification_id",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "notification_id"
+ }
+ }
+ },
+ {
+ "name": "conversation_id",
+ "ordinal": 2,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "conversation_id"
+ }
+ }
+ },
+ {
+ "name": "sender_id",
+ "ordinal": 3,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "sender_id"
+ }
+ }
+ },
+ {
+ "name": "sender_name!: String",
+ "ordinal": 4,
+ "type_info": "Text",
+ "origin": "Expression"
+ },
+ {
+ "name": "avatar_svg_compressed",
+ "ordinal": 5,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "avatar_svg_compressed"
+ }
+ }
+ },
+ {
+ "name": "sender_profile_counter",
+ "ordinal": 6,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "sender_profile_counter"
+ }
+ }
+ },
+ {
+ "name": "conversation_name",
+ "ordinal": 7,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "group_name"
+ }
+ }
+ },
+ {
+ "name": "is_direct_chat!: i64",
+ "ordinal": 8,
+ "type_info": "Integer",
+ "origin": "Expression"
+ },
+ {
+ "name": "message_id",
+ "ordinal": 9,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "message_id"
+ }
+ }
+ },
+ {
+ "name": "kind",
+ "ordinal": 10,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "kind"
+ }
+ }
+ },
+ {
+ "name": "content",
+ "ordinal": 11,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "content"
+ }
+ }
+ },
+ {
+ "name": "created_at",
+ "ordinal": 12,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "notification_outbox",
+ "name": "created_at"
+ }
+ }
+ },
+ {
+ "name": "target_message_type",
+ "ordinal": 13,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "type"
+ }
+ }
+ },
+ {
+ "name": "target_media_type",
+ "ordinal": 14,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "type"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ false,
+ false,
+ true,
+ false,
+ true,
+ false,
+ true,
+ false,
+ true,
+ false,
+ true,
+ true
+ ]
+ },
+ "hash": "7ee254f54718b616580ea6ca979679f8ec8065490cf0ffd0a8f44818c1f57167"
+}
diff --git a/rust/.sqlx/query-81135d8e9734594afc97deee965b5b2db4edab812e94912b4a70c3a83ad91933.json b/rust/.sqlx/query-81135d8e9734594afc97deee965b5b2db4edab812e94912b4a70c3a83ad91933.json
deleted file mode 100644
index ff979a32..00000000
--- a/rust/.sqlx/query-81135d8e9734594afc97deee965b5b2db4edab812e94912b4a70c3a83ad91933.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE messages\n SET opened_at = ?, opened_by_all = ?\n WHERE message_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 3
- },
- "nullable": []
- },
- "hash": "81135d8e9734594afc97deee965b5b2db4edab812e94912b4a70c3a83ad91933"
-}
diff --git a/rust/.sqlx/query-815f8d36932a7cad2f415eef0e2252acb4f823132f89b42d697c83be5beca994.json b/rust/.sqlx/query-815f8d36932a7cad2f415eef0e2252acb4f823132f89b42d697c83be5beca994.json
new file mode 100644
index 00000000..fec99aa2
--- /dev/null
+++ b/rust/.sqlx/query-815f8d36932a7cad2f415eef0e2252acb4f823132f89b42d697c83be5beca994.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT user_id, recovery_secret_share FROM contacts\n WHERE recovery_is_trusted_friend = 1\n AND recovery_last_heartbeat IS NULL\n AND recovery_secret_share IS NOT NULL",
+ "describe": {
+ "columns": [
+ {
+ "name": "user_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_id"
+ }
+ }
+ },
+ {
+ "name": "recovery_secret_share",
+ "ordinal": 1,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_secret_share"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false,
+ true
+ ]
+ },
+ "hash": "815f8d36932a7cad2f415eef0e2252acb4f823132f89b42d697c83be5beca994"
+}
diff --git a/rust/.sqlx/query-83913e710fbd04e98cd6de21e7c917ed6472ef90230a10a69f74cb07d31a99fa.json b/rust/.sqlx/query-83913e710fbd04e98cd6de21e7c917ed6472ef90230a10a69f74cb07d31a99fa.json
new file mode 100644
index 00000000..e9f6ed5c
--- /dev/null
+++ b/rust/.sqlx/query-83913e710fbd04e98cd6de21e7c917ed6472ef90230a10a69f74cb07d31a99fa.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM receipts WHERE contact_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "83913e710fbd04e98cd6de21e7c917ed6472ef90230a10a69f74cb07d31a99fa"
+}
diff --git a/rust/.sqlx/query-84098bf2101af287c203998ebf38377a472c93b4baf0a6970c8f8075b7aeae33.json b/rust/.sqlx/query-84098bf2101af287c203998ebf38377a472c93b4baf0a6970c8f8075b7aeae33.json
new file mode 100644
index 00000000..8c2a450c
--- /dev/null
+++ b/rust/.sqlx/query-84098bf2101af287c203998ebf38377a472c93b4baf0a6970c8f8075b7aeae33.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO user_discovery_other_promotions (\n from_contact_id, promotion_id, public_id, threshold,\n announcement_share, public_key_verified_timestamp\n ) VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(from_contact_id, public_id) DO UPDATE SET\n promotion_id = excluded.promotion_id,\n threshold = excluded.threshold,\n announcement_share = excluded.announcement_share,\n public_key_verified_timestamp = excluded.public_key_verified_timestamp\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 6
+ },
+ "nullable": []
+ },
+ "hash": "84098bf2101af287c203998ebf38377a472c93b4baf0a6970c8f8075b7aeae33"
+}
diff --git a/rust/.sqlx/query-84c77f8397fdf0dcf0d3c9ccab7f3854d2d607de0662b9e5f7d279f5e9417ebf.json b/rust/.sqlx/query-84c77f8397fdf0dcf0d3c9ccab7f3854d2d607de0662b9e5f7d279f5e9417ebf.json
deleted file mode 100644
index 6b5d5241..00000000
--- a/rust/.sqlx/query-84c77f8397fdf0dcf0d3c9ccab7f3854d2d607de0662b9e5f7d279f5e9417ebf.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE contacts\n SET requested = 1, deleted_by_user = 0\n WHERE user_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 1
- },
- "nullable": []
- },
- "hash": "84c77f8397fdf0dcf0d3c9ccab7f3854d2d607de0662b9e5f7d279f5e9417ebf"
-}
diff --git a/rust/.sqlx/query-84efd2ee67643544a39e5bc79b33db8ddb3697ee130fa93f3c8ea0665c449d42.json b/rust/.sqlx/query-84efd2ee67643544a39e5bc79b33db8ddb3697ee130fa93f3c8ea0665c449d42.json
new file mode 100644
index 00000000..7bf7a0e0
--- /dev/null
+++ b/rust/.sqlx/query-84efd2ee67643544a39e5bc79b33db8ddb3697ee130fa93f3c8ea0665c449d42.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT group_public_key FROM group_members WHERE group_id = ? AND contact_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "group_public_key",
+ "ordinal": 0,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "group_members",
+ "name": "group_public_key"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "84efd2ee67643544a39e5bc79b33db8ddb3697ee130fa93f3c8ea0665c449d42"
+}
diff --git a/rust/.sqlx/query-852a504566c7e3f6faab472a5c0c9bdbf88b4c308097d8fea31f122ac980144c.json b/rust/.sqlx/query-852a504566c7e3f6faab472a5c0c9bdbf88b4c308097d8fea31f122ac980144c.json
new file mode 100644
index 00000000..29b71c0e
--- /dev/null
+++ b/rust/.sqlx/query-852a504566c7e3f6faab472a5c0c9bdbf88b4c308097d8fea31f122ac980144c.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO api_outbox(sequence_id, operation_kind, payload) VALUES(?, ?, ?)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "852a504566c7e3f6faab472a5c0c9bdbf88b4c308097d8fea31f122ac980144c"
+}
diff --git a/rust/.sqlx/query-856ba1dc64d80914973128cefd776a9442a6a73ba94145c85245780a86b241e8.json b/rust/.sqlx/query-856ba1dc64d80914973128cefd776a9442a6a73ba94145c85245780a86b241e8.json
new file mode 100644
index 00000000..2fb5192b
--- /dev/null
+++ b/rust/.sqlx/query-856ba1dc64d80914973128cefd776a9442a6a73ba94145c85245780a86b241e8.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE contacts\n SET user_discovery_excluded = ?, user_discovery_version = NULL\n WHERE user_id = ?\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "856ba1dc64d80914973128cefd776a9442a6a73ba94145c85245780a86b241e8"
+}
diff --git a/rust/.sqlx/query-869eb7007b683a3415ac0bc00e929aa117302944f52b95f6dece05c52b4fc905.json b/rust/.sqlx/query-869eb7007b683a3415ac0bc00e929aa117302944f52b95f6dece05c52b4fc905.json
new file mode 100644
index 00000000..c0cc5d3d
--- /dev/null
+++ b/rust/.sqlx/query-869eb7007b683a3415ac0bc00e929aa117302944f52b95f6dece05c52b4fc905.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM key_verifications WHERE contact_id = ? AND type = 'manualTest'",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "869eb7007b683a3415ac0bc00e929aa117302944f52b95f6dece05c52b4fc905"
+}
diff --git a/rust/.sqlx/query-86f467ce150a319885c90ca3e582f2cc26f097a9094ddf600c3d7462f988858e.json b/rust/.sqlx/query-86f467ce150a319885c90ca3e582f2cc26f097a9094ddf600c3d7462f988858e.json
new file mode 100644
index 00000000..f6e6d393
--- /dev/null
+++ b/rust/.sqlx/query-86f467ce150a319885c90ca3e582f2cc26f097a9094ddf600c3d7462f988858e.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE receipts\n SET mark_for_retry = CAST(strftime('%s', 'now') AS INTEGER)\n WHERE contact_id = ? AND mark_for_retry IS NULL\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "86f467ce150a319885c90ca3e582f2cc26f097a9094ddf600c3d7462f988858e"
+}
diff --git a/rust/.sqlx/query-87eac4d0b721d8726db7fe81b8842c877dbc1de54b276baca6f4f90d58e77406.json b/rust/.sqlx/query-87eac4d0b721d8726db7fe81b8842c877dbc1de54b276baca6f4f90d58e77406.json
new file mode 100644
index 00000000..499dabfc
--- /dev/null
+++ b/rust/.sqlx/query-87eac4d0b721d8726db7fe81b8842c877dbc1de54b276baca6f4f90d58e77406.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT is_direct_chat FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "is_direct_chat",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "is_direct_chat"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "87eac4d0b721d8726db7fe81b8842c877dbc1de54b276baca6f4f90d58e77406"
+}
diff --git a/rust/.sqlx/query-b3242f4964184d187d818d2e112aa826377c5ce5e09749f780c3a6a9870157d9.json b/rust/.sqlx/query-8ec24eb618ebb570820cc72e5f8f6dafa2efb86d5054b1f38ca2b6ccfa95a79d.json
similarity index 52%
rename from rust/.sqlx/query-b3242f4964184d187d818d2e112aa826377c5ce5e09749f780c3a6a9870157d9.json
rename to rust/.sqlx/query-8ec24eb618ebb570820cc72e5f8f6dafa2efb86d5054b1f38ca2b6ccfa95a79d.json
index 7b8012e9..c7644191 100644
--- a/rust/.sqlx/query-b3242f4964184d187d818d2e112aa826377c5ce5e09749f780c3a6a9870157d9.json
+++ b/rust/.sqlx/query-8ec24eb618ebb570820cc72e5f8f6dafa2efb86d5054b1f38ca2b6ccfa95a79d.json
@@ -1,12 +1,12 @@
{
"db_name": "SQLite",
- "query": "\n INSERT INTO contacts (user_id, username)\n VALUES (1, 'one'), (2, 'two')\n ",
+ "query": "DELETE FROM group_members WHERE group_id = ?",
"describe": {
"columns": [],
"parameters": {
- "Right": 0
+ "Right": 1
},
"nullable": []
},
- "hash": "b3242f4964184d187d818d2e112aa826377c5ce5e09749f780c3a6a9870157d9"
+ "hash": "8ec24eb618ebb570820cc72e5f8f6dafa2efb86d5054b1f38ca2b6ccfa95a79d"
}
diff --git a/rust/.sqlx/query-6c01273c757a772f2c2726b75a95df7f39ee9353043ec309fe128183fe1cdcd6.json b/rust/.sqlx/query-8f818e8dc1be8e9bcb4fd908e3cf47fbbfe50c98b7fa506aee637e06e1ab8029.json
similarity index 67%
rename from rust/.sqlx/query-6c01273c757a772f2c2726b75a95df7f39ee9353043ec309fe128183fe1cdcd6.json
rename to rust/.sqlx/query-8f818e8dc1be8e9bcb4fd908e3cf47fbbfe50c98b7fa506aee637e06e1ab8029.json
index 94a3b1a1..3a06ac5e 100644
--- a/rust/.sqlx/query-6c01273c757a772f2c2726b75a95df7f39ee9353043ec309fe128183fe1cdcd6.json
+++ b/rust/.sqlx/query-8f818e8dc1be8e9bcb4fd908e3cf47fbbfe50c98b7fa506aee637e06e1ab8029.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "SELECT COUNT(*) FROM reactions",
+ "query": "SELECT COUNT(*) FROM user_discovery_shares",
"describe": {
"columns": [
{
@@ -17,5 +17,5 @@
false
]
},
- "hash": "6c01273c757a772f2c2726b75a95df7f39ee9353043ec309fe128183fe1cdcd6"
+ "hash": "8f818e8dc1be8e9bcb4fd908e3cf47fbbfe50c98b7fa506aee637e06e1ab8029"
}
diff --git a/rust/.sqlx/query-90cb5a536c050635d321454a8bf027bf836e5b52681edce95a53d8175cceee48.json b/rust/.sqlx/query-90cb5a536c050635d321454a8bf027bf836e5b52681edce95a53d8175cceee48.json
new file mode 100644
index 00000000..b0597612
--- /dev/null
+++ b/rust/.sqlx/query-90cb5a536c050635d321454a8bf027bf836e5b52681edce95a53d8175cceee48.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO group_histories(\n group_history_id,\n group_id,\n contact_id,\n old_group_name,\n new_group_name,\n type\n ) VALUES (?, ?, ?, ?, ?, ?)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 6
+ },
+ "nullable": []
+ },
+ "hash": "90cb5a536c050635d321454a8bf027bf836e5b52681edce95a53d8175cceee48"
+}
diff --git a/rust/.sqlx/query-90fddc5282c6f13b56cd08e39dd24eec89963291cf50ad69ad13916953ec0539.json b/rust/.sqlx/query-90fddc5282c6f13b56cd08e39dd24eec89963291cf50ad69ad13916953ec0539.json
new file mode 100644
index 00000000..9a1539ca
--- /dev/null
+++ b/rust/.sqlx/query-90fddc5282c6f13b56cd08e39dd24eec89963291cf50ad69ad13916953ec0539.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT type FROM media_files WHERE media_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "type",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "type"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "90fddc5282c6f13b56cd08e39dd24eec89963291cf50ad69ad13916953ec0539"
+}
diff --git a/rust/.sqlx/query-93e9fb25a53d3de02840e86d0c450c364e2e4c3b4af9d015fb2fbb87770e703f.json b/rust/.sqlx/query-93e9fb25a53d3de02840e86d0c450c364e2e4c3b4af9d015fb2fbb87770e703f.json
deleted file mode 100644
index dace5654..00000000
--- a/rust/.sqlx/query-93e9fb25a53d3de02840e86d0c450c364e2e4c3b4af9d015fb2fbb87770e703f.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO groups(group_id, group_name, is_direct_chat, joined_group)\n VALUES (?, ?, 1, 1)\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "93e9fb25a53d3de02840e86d0c450c364e2e4c3b4af9d015fb2fbb87770e703f"
-}
diff --git a/rust/.sqlx/query-94f378d23149fc82d31b600b76ecd89af66301c64b0a30d3b82f285f5647e5ac.json b/rust/.sqlx/query-94f378d23149fc82d31b600b76ecd89af66301c64b0a30d3b82f285f5647e5ac.json
new file mode 100644
index 00000000..d78781a8
--- /dev/null
+++ b/rust/.sqlx/query-94f378d23149fc82d31b600b76ecd89af66301c64b0a30d3b82f285f5647e5ac.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO contacts(user_id, username, accepted, requested) VALUES (?, '[deleted]', 1, 0)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "94f378d23149fc82d31b600b76ecd89af66301c64b0a30d3b82f285f5647e5ac"
+}
diff --git a/rust/.sqlx/query-96830f8fde36c6db93fb959c0e140effefbb271e84813a4d85fe255e90b28695.json b/rust/.sqlx/query-96830f8fde36c6db93fb959c0e140effefbb271e84813a4d85fe255e90b28695.json
new file mode 100644
index 00000000..79f3b5b9
--- /dev/null
+++ b/rust/.sqlx/query-96830f8fde36c6db93fb959c0e140effefbb271e84813a4d85fe255e90b28695.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT username FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "username",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "username"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "96830f8fde36c6db93fb959c0e140effefbb271e84813a4d85fe255e90b28695"
+}
diff --git a/rust/.sqlx/query-96a6b8415b84654e0bc41298aa4ca886e42740dddf2e4db1a4a289e4f75a1ff4.json b/rust/.sqlx/query-96a6b8415b84654e0bc41298aa4ca886e42740dddf2e4db1a4a289e4f75a1ff4.json
deleted file mode 100644
index 926f2c7e..00000000
--- a/rust/.sqlx/query-96a6b8415b84654e0bc41298aa4ca886e42740dddf2e4db1a4a289e4f75a1ff4.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n SELECT EXISTS(\n SELECT 1\n FROM received_receipts\n WHERE receipt_id = ?\n )\n ",
- "describe": {
- "columns": [
- {
- "name": "EXISTS(\n SELECT 1\n FROM received_receipts\n WHERE receipt_id = ?\n )",
- "ordinal": 0,
- "type_info": "Integer",
- "origin": "Expression"
- }
- ],
- "parameters": {
- "Right": 1
- },
- "nullable": [
- false
- ]
- },
- "hash": "96a6b8415b84654e0bc41298aa4ca886e42740dddf2e4db1a4a289e4f75a1ff4"
-}
diff --git a/rust/.sqlx/query-96dfd5c9fea55bbc19f096c6d3d2d15cbf4bd389ccca0ea4831ddbd956b711c5.json b/rust/.sqlx/query-96dfd5c9fea55bbc19f096c6d3d2d15cbf4bd389ccca0ea4831ddbd956b711c5.json
new file mode 100644
index 00000000..2a637e29
--- /dev/null
+++ b/rust/.sqlx/query-96dfd5c9fea55bbc19f096c6d3d2d15cbf4bd389ccca0ea4831ddbd956b711c5.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT OR IGNORE INTO contacts(user_id, username, signal_version, accepted, requested)\n VALUES (?, ?, ?, 0, 1)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "96dfd5c9fea55bbc19f096c6d3d2d15cbf4bd389ccca0ea4831ddbd956b711c5"
+}
diff --git a/rust/.sqlx/query-986b8712045ce40ce4c6cd7b46f28472f0d22b0b37453c5d53bab12aeb153553.json b/rust/.sqlx/query-986b8712045ce40ce4c6cd7b46f28472f0d22b0b37453c5d53bab12aeb153553.json
new file mode 100644
index 00000000..c11fd632
--- /dev/null
+++ b/rust/.sqlx/query-986b8712045ce40ce4c6cd7b46f28472f0d22b0b37453c5d53bab12aeb153553.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT group_id FROM group_members WHERE contact_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "group_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "group_members",
+ "name": "group_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "986b8712045ce40ce4c6cd7b46f28472f0d22b0b37453c5d53bab12aeb153553"
+}
diff --git a/rust/.sqlx/query-987d30f5e96500da929cd3417030c284bc17657b9ea34e25e80cb5672f3c5abc.json b/rust/.sqlx/query-987d30f5e96500da929cd3417030c284bc17657b9ea34e25e80cb5672f3c5abc.json
new file mode 100644
index 00000000..bd9f6aed
--- /dev/null
+++ b/rust/.sqlx/query-987d30f5e96500da929cd3417030c284bc17657b9ea34e25e80cb5672f3c5abc.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE group_members SET member_state = ? WHERE group_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "987d30f5e96500da929cd3417030c284bc17657b9ea34e25e80cb5672f3c5abc"
+}
diff --git a/rust/.sqlx/query-9a69d1998079dc207b5c9a4b62ff804b96475d3b5b25ef55822011610967ace2.json b/rust/.sqlx/query-9a69d1998079dc207b5c9a4b62ff804b96475d3b5b25ef55822011610967ace2.json
new file mode 100644
index 00000000..7d00687e
--- /dev/null
+++ b/rust/.sqlx/query-9a69d1998079dc207b5c9a4b62ff804b96475d3b5b25ef55822011610967ace2.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO user_discovery_own_promotions(contact_id, promotion) VALUES (?, ?)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "9a69d1998079dc207b5c9a4b62ff804b96475d3b5b25ef55822011610967ace2"
+}
diff --git a/rust/.sqlx/query-9bc6a3746c3a8df3b089a34a4e70098d5691f4511904fe17ab67885c07a7dbe8.json b/rust/.sqlx/query-9bc6a3746c3a8df3b089a34a4e70098d5691f4511904fe17ab67885c07a7dbe8.json
new file mode 100644
index 00000000..9d3dd508
--- /dev/null
+++ b/rust/.sqlx/query-9bc6a3746c3a8df3b089a34a4e70098d5691f4511904fe17ab67885c07a7dbe8.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT recovery_last_heartbeat FROM contacts WHERE user_id = 8",
+ "describe": {
+ "columns": [
+ {
+ "name": "recovery_last_heartbeat",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_last_heartbeat"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "9bc6a3746c3a8df3b089a34a4e70098d5691f4511904fe17ab67885c07a7dbe8"
+}
diff --git a/rust/.sqlx/query-9de60ce6ee3cf9449d5142a94637426faa727329c78224909732cde2205cf631.json b/rust/.sqlx/query-9de60ce6ee3cf9449d5142a94637426faa727329c78224909732cde2205cf631.json
new file mode 100644
index 00000000..ef8154a2
--- /dev/null
+++ b/rust/.sqlx/query-9de60ce6ee3cf9449d5142a94637426faa727329c78224909732cde2205cf631.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE group_members SET group_public_key = ? WHERE group_id = ? AND contact_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "9de60ce6ee3cf9449d5142a94637426faa727329c78224909732cde2205cf631"
+}
diff --git a/rust/.sqlx/query-9e596b383a0af665ed0645792af43636345cf9372c0d4005df9d179f1004a619.json b/rust/.sqlx/query-9e596b383a0af665ed0645792af43636345cf9372c0d4005df9d179f1004a619.json
new file mode 100644
index 00000000..bb6d4f8e
--- /dev/null
+++ b/rust/.sqlx/query-9e596b383a0af665ed0645792af43636345cf9372c0d4005df9d179f1004a619.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE media_files SET download_state = 'pending' WHERE download_state = 'downloading'",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": []
+ },
+ "hash": "9e596b383a0af665ed0645792af43636345cf9372c0d4005df9d179f1004a619"
+}
diff --git a/rust/.sqlx/query-9e7086794e7e4cb9b9db781830423875720f5458d209f9ff4ecbed856dd45e08.json b/rust/.sqlx/query-9e7086794e7e4cb9b9db781830423875720f5458d209f9ff4ecbed856dd45e08.json
new file mode 100644
index 00000000..e709250e
--- /dev/null
+++ b/rust/.sqlx/query-9e7086794e7e4cb9b9db781830423875720f5458d209f9ff4ecbed856dd45e08.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT sender_profile_counter FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "sender_profile_counter",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "sender_profile_counter"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "9e7086794e7e4cb9b9db781830423875720f5458d209f9ff4ecbed856dd45e08"
+}
diff --git a/rust/.sqlx/query-9d236335cde01fe13d6836309e7a4bcb8485282d2230d2504350642ce1236169.json b/rust/.sqlx/query-a0e47f74f1f40f409dd51e4a3cc2638d5ca30a0f7db6fd3d620c597ad630cecc.json
similarity index 58%
rename from rust/.sqlx/query-9d236335cde01fe13d6836309e7a4bcb8485282d2230d2504350642ce1236169.json
rename to rust/.sqlx/query-a0e47f74f1f40f409dd51e4a3cc2638d5ca30a0f7db6fd3d620c597ad630cecc.json
index 45809abf..3542fb8f 100644
--- a/rust/.sqlx/query-9d236335cde01fe13d6836309e7a4bcb8485282d2230d2504350642ce1236169.json
+++ b/rust/.sqlx/query-a0e47f74f1f40f409dd51e4a3cc2638d5ca30a0f7db6fd3d620c597ad630cecc.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n SELECT promotion\n FROM user_discovery_own_promotions\n WHERE contact_id = ?\n ORDER BY version_id DESC\n LIMIT 1\n ",
+ "query": "SELECT promotion FROM user_discovery_own_promotions WHERE version_id > ?",
"describe": {
"columns": [
{
@@ -22,5 +22,5 @@
false
]
},
- "hash": "9d236335cde01fe13d6836309e7a4bcb8485282d2230d2504350642ce1236169"
+ "hash": "a0e47f74f1f40f409dd51e4a3cc2638d5ca30a0f7db6fd3d620c597ad630cecc"
}
diff --git a/rust/.sqlx/query-a0e60588e599879c2d5b5c40088f3a0e6caf0061e009784d225d0b863487c2aa.json b/rust/.sqlx/query-a0e60588e599879c2d5b5c40088f3a0e6caf0061e009784d225d0b863487c2aa.json
new file mode 100644
index 00000000..1bfc7fc9
--- /dev/null
+++ b/rust/.sqlx/query-a0e60588e599879c2d5b5c40088f3a0e6caf0061e009784d225d0b863487c2aa.json
@@ -0,0 +1,50 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT sender_id, type, additional_message_data FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "sender_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "sender_id"
+ }
+ }
+ },
+ {
+ "name": "type",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "type"
+ }
+ }
+ },
+ {
+ "name": "additional_message_data",
+ "ordinal": 2,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "additional_message_data"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true,
+ false,
+ true
+ ]
+ },
+ "hash": "a0e60588e599879c2d5b5c40088f3a0e6caf0061e009784d225d0b863487c2aa"
+}
diff --git a/rust/.sqlx/query-a2b5abc3e3b81290f8b86db19f4e14ad964e8b614c7698e864da79e906c3737c.json b/rust/.sqlx/query-a2b5abc3e3b81290f8b86db19f4e14ad964e8b614c7698e864da79e906c3737c.json
new file mode 100644
index 00000000..92bd3cf7
--- /dev/null
+++ b/rust/.sqlx/query-a2b5abc3e3b81290f8b86db19f4e14ad964e8b614c7698e864da79e906c3737c.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM receipts WHERE receipt_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "a2b5abc3e3b81290f8b86db19f4e14ad964e8b614c7698e864da79e906c3737c"
+}
diff --git a/rust/.sqlx/query-a2e8523eb0be989585370d3d8b1d9a5586a1f75b84a609c61fd2f18faa67832f.json b/rust/.sqlx/query-a2e8523eb0be989585370d3d8b1d9a5586a1f75b84a609c61fd2f18faa67832f.json
new file mode 100644
index 00000000..89ec1a50
--- /dev/null
+++ b/rust/.sqlx/query-a2e8523eb0be989585370d3d8b1d9a5586a1f75b84a609c61fd2f18faa67832f.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE receipts SET\n ack_by_server_at = CAST(strftime('%s', 'now') AS INTEGER),\n retry_count = retry_count + 1,\n last_retry = CAST(strftime('%s', 'now') AS INTEGER),\n mark_for_retry = NULL\n WHERE receipt_id = ?\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "a2e8523eb0be989585370d3d8b1d9a5586a1f75b84a609c61fd2f18faa67832f"
+}
diff --git a/rust/.sqlx/query-500c182a7888c7b36c257936ba188aff109a5dac250d5402f33b55ec9d127f76.json b/rust/.sqlx/query-a4277bba7d52fe5f3d87a5d0148d46fea6e798610c1248f95cd919e10257d4ad.json
similarity index 53%
rename from rust/.sqlx/query-500c182a7888c7b36c257936ba188aff109a5dac250d5402f33b55ec9d127f76.json
rename to rust/.sqlx/query-a4277bba7d52fe5f3d87a5d0148d46fea6e798610c1248f95cd919e10257d4ad.json
index 4def1f40..ade00ef5 100644
--- a/rust/.sqlx/query-500c182a7888c7b36c257936ba188aff109a5dac250d5402f33b55ec9d127f76.json
+++ b/rust/.sqlx/query-a4277bba7d52fe5f3d87a5d0148d46fea6e798610c1248f95cd919e10257d4ad.json
@@ -1,21 +1,21 @@
{
"db_name": "SQLite",
- "query": "\n SELECT EXISTS(\n SELECT 1\n FROM received_receipts\n WHERE receipt_id = ?\n )\n ",
+ "query": "\n SELECT EXISTS(\n SELECT 1\n FROM group_members\n WHERE group_id = ? AND contact_id = ?\n )\n ",
"describe": {
"columns": [
{
- "name": "EXISTS(\n SELECT 1\n FROM received_receipts\n WHERE receipt_id = ?\n )",
+ "name": "EXISTS(\n SELECT 1\n FROM group_members\n WHERE group_id = ? AND contact_id = ?\n )",
"ordinal": 0,
"type_info": "Integer",
"origin": "Expression"
}
],
"parameters": {
- "Right": 1
+ "Right": 2
},
"nullable": [
false
]
},
- "hash": "500c182a7888c7b36c257936ba188aff109a5dac250d5402f33b55ec9d127f76"
+ "hash": "a4277bba7d52fe5f3d87a5d0148d46fea6e798610c1248f95cd919e10257d4ad"
}
diff --git a/rust/.sqlx/query-a54b3db0042be8b336a097a62a27ceb79c2983736ea880483b1be71ae7a2af94.json b/rust/.sqlx/query-a54b3db0042be8b336a097a62a27ceb79c2983736ea880483b1be71ae7a2af94.json
new file mode 100644
index 00000000..9fdc959e
--- /dev/null
+++ b/rust/.sqlx/query-a54b3db0042be8b336a097a62a27ceb79c2983736ea880483b1be71ae7a2af94.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO groups(\n group_id,\n group_name,\n is_group_admin,\n state_encryption_key,\n state_version_id,\n my_group_private_key,\n joined_group\n ) VALUES (?, ?, 1, ?, 1, ?, 1)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 4
+ },
+ "nullable": []
+ },
+ "hash": "a54b3db0042be8b336a097a62a27ceb79c2983736ea880483b1be71ae7a2af94"
+}
diff --git a/rust/.sqlx/query-a55a2f3891948d7260a1fa7cb250596e8dca1e86c487657d0043f5e369131b0b.json b/rust/.sqlx/query-a55a2f3891948d7260a1fa7cb250596e8dca1e86c487657d0043f5e369131b0b.json
new file mode 100644
index 00000000..24bb1dc7
--- /dev/null
+++ b/rust/.sqlx/query-a55a2f3891948d7260a1fa7cb250596e8dca1e86c487657d0043f5e369131b0b.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT username, display_name FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "username",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "username"
+ }
+ }
+ },
+ {
+ "name": "display_name",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "display_name"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ true
+ ]
+ },
+ "hash": "a55a2f3891948d7260a1fa7cb250596e8dca1e86c487657d0043f5e369131b0b"
+}
diff --git a/rust/.sqlx/query-a57dd1863c2d8db90075c5dc0b3550711ad15801756d571dfa97c9907969348b.json b/rust/.sqlx/query-a57dd1863c2d8db90075c5dc0b3550711ad15801756d571dfa97c9907969348b.json
new file mode 100644
index 00000000..ae81de22
--- /dev/null
+++ b/rust/.sqlx/query-a57dd1863c2d8db90075c5dc0b3550711ad15801756d571dfa97c9907969348b.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET blocked = ? WHERE user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "a57dd1863c2d8db90075c5dc0b3550711ad15801756d571dfa97c9907969348b"
+}
diff --git a/rust/.sqlx/query-a8edfc3a445195355f8324396c28396a3298fecd579a5e9fbc84ae4974685724.json b/rust/.sqlx/query-a8edfc3a445195355f8324396c28396a3298fecd579a5e9fbc84ae4974685724.json
new file mode 100644
index 00000000..178c95a0
--- /dev/null
+++ b/rust/.sqlx/query-a8edfc3a445195355f8324396c28396a3298fecd579a5e9fbc84ae4974685724.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT group_id FROM groups WHERE is_direct_chat = 0 ORDER BY rowid DESC LIMIT 1",
+ "describe": {
+ "columns": [
+ {
+ "name": "group_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "group_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "a8edfc3a445195355f8324396c28396a3298fecd579a5e9fbc84ae4974685724"
+}
diff --git a/rust/.sqlx/query-a90a25b72f9fb6269a4943fda3da3c8cc58b2d692fb79e9445e070d949afc9ab.json b/rust/.sqlx/query-a90a25b72f9fb6269a4943fda3da3c8cc58b2d692fb79e9445e070d949afc9ab.json
deleted file mode 100644
index 1d7abca1..00000000
--- a/rust/.sqlx/query-a90a25b72f9fb6269a4943fda3da3c8cc58b2d692fb79e9445e070d949afc9ab.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO messages(\n group_id,\n message_id,\n sender_id,\n type,\n additional_message_data,\n created_at,\n ack_by_server\n ) VALUES (?, ?, ?, ?, ?, ?, CAST(strftime('%s', 'now') AS INTEGER))\n ON CONFLICT(message_id) DO NOTHING\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 6
- },
- "nullable": []
- },
- "hash": "a90a25b72f9fb6269a4943fda3da3c8cc58b2d692fb79e9445e070d949afc9ab"
-}
diff --git a/rust/.sqlx/query-a93838b8031d794e21141424f5a7856c33d05ff01422e040df34783dffcc9b91.json b/rust/.sqlx/query-a93838b8031d794e21141424f5a7856c33d05ff01422e040df34783dffcc9b91.json
new file mode 100644
index 00000000..7b63f485
--- /dev/null
+++ b/rust/.sqlx/query-a93838b8031d794e21141424f5a7856c33d05ff01422e040df34783dffcc9b91.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT recovery_contacts_secret_share, recovery_contacts_threshold FROM contacts WHERE user_id = 7",
+ "describe": {
+ "columns": [
+ {
+ "name": "recovery_contacts_secret_share",
+ "ordinal": 0,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_contacts_secret_share"
+ }
+ }
+ },
+ {
+ "name": "recovery_contacts_threshold",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_contacts_threshold"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ true,
+ true
+ ]
+ },
+ "hash": "a93838b8031d794e21141424f5a7856c33d05ff01422e040df34783dffcc9b91"
+}
diff --git a/rust/.sqlx/query-abdea64654e2f35b26a349227190a84fb4eebe583da68e21353005bd87c15058.json b/rust/.sqlx/query-abdea64654e2f35b26a349227190a84fb4eebe583da68e21353005bd87c15058.json
new file mode 100644
index 00000000..6d1feead
--- /dev/null
+++ b/rust/.sqlx/query-abdea64654e2f35b26a349227190a84fb4eebe583da68e21353005bd87c15058.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(*) FROM notification_outbox WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "COUNT(*)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "abdea64654e2f35b26a349227190a84fb4eebe583da68e21353005bd87c15058"
+}
diff --git a/rust/.sqlx/query-ac89da5b76bdd44d6c3c8901b2d99ff9352ab497a2abb687a1b8ef3cb09701ab.json b/rust/.sqlx/query-ac89da5b76bdd44d6c3c8901b2d99ff9352ab497a2abb687a1b8ef3cb09701ab.json
deleted file mode 100644
index 156adfdb..00000000
--- a/rust/.sqlx/query-ac89da5b76bdd44d6c3c8901b2d99ff9352ab497a2abb687a1b8ef3cb09701ab.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n SELECT EXISTS(\n SELECT 1\n FROM group_members\n WHERE group_id = ? AND contact_id = ?\n )\n ",
- "describe": {
- "columns": [
- {
- "name": "EXISTS(\n SELECT 1\n FROM group_members\n WHERE group_id = ? AND contact_id = ?\n )",
- "ordinal": 0,
- "type_info": "Integer",
- "origin": "Expression"
- }
- ],
- "parameters": {
- "Right": 2
- },
- "nullable": [
- false
- ]
- },
- "hash": "ac89da5b76bdd44d6c3c8901b2d99ff9352ab497a2abb687a1b8ef3cb09701ab"
-}
diff --git a/rust/.sqlx/query-ada4acec27f1fcbac9dcf87d5a2a95845f759c05772b82a8717a4241801bf785.json b/rust/.sqlx/query-ada4acec27f1fcbac9dcf87d5a2a95845f759c05772b82a8717a4241801bf785.json
new file mode 100644
index 00000000..541f34cb
--- /dev/null
+++ b/rust/.sqlx/query-ada4acec27f1fcbac9dcf87d5a2a95845f759c05772b82a8717a4241801bf785.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(*) FROM notification_outbox WHERE event_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "COUNT(*)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "ada4acec27f1fcbac9dcf87d5a2a95845f759c05772b82a8717a4241801bf785"
+}
diff --git a/rust/.sqlx/query-ae43489000b353b7d3252a7626d75239dca0a96c2645f885df5bd2738d8035b7.json b/rust/.sqlx/query-ae43489000b353b7d3252a7626d75239dca0a96c2645f885df5bd2738d8035b7.json
new file mode 100644
index 00000000..d84f5076
--- /dev/null
+++ b/rust/.sqlx/query-ae43489000b353b7d3252a7626d75239dca0a96c2645f885df5bd2738d8035b7.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT download_state FROM media_files WHERE media_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "download_state",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "download_state"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "ae43489000b353b7d3252a7626d75239dca0a96c2645f885df5bd2738d8035b7"
+}
diff --git a/rust/.sqlx/query-aeed49e886c108b34f98af86d5a9b00536458ebf6f45ab47a265fc0faff78a00.json b/rust/.sqlx/query-aeed49e886c108b34f98af86d5a9b00536458ebf6f45ab47a265fc0faff78a00.json
new file mode 100644
index 00000000..a2dd20e6
--- /dev/null
+++ b/rust/.sqlx/query-aeed49e886c108b34f98af86d5a9b00536458ebf6f45ab47a265fc0faff78a00.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT additional_message_data FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "additional_message_data",
+ "ordinal": 0,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "additional_message_data"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "aeed49e886c108b34f98af86d5a9b00536458ebf6f45ab47a265fc0faff78a00"
+}
diff --git a/rust/.sqlx/query-b28e87ff6d1286b511ec7923b3dd8343423b6f4c01a6825e4a1b52b39047b1c7.json b/rust/.sqlx/query-b28e87ff6d1286b511ec7923b3dd8343423b6f4c01a6825e4a1b52b39047b1c7.json
new file mode 100644
index 00000000..f8c7f67d
--- /dev/null
+++ b/rust/.sqlx/query-b28e87ff6d1286b511ec7923b3dd8343423b6f4c01a6825e4a1b52b39047b1c7.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(*) FROM contacts WHERE accepted = 1",
+ "describe": {
+ "columns": [
+ {
+ "name": "COUNT(*)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "b28e87ff6d1286b511ec7923b3dd8343423b6f4c01a6825e4a1b52b39047b1c7"
+}
diff --git a/rust/.sqlx/query-b375c5808bf4526a65d96c6262cf03bdb10c4c2dd00a4a57ab49c27ff7fa8d05.json b/rust/.sqlx/query-b375c5808bf4526a65d96c6262cf03bdb10c4c2dd00a4a57ab49c27ff7fa8d05.json
deleted file mode 100644
index 97f5add9..00000000
--- a/rust/.sqlx/query-b375c5808bf4526a65d96c6262cf03bdb10c4c2dd00a4a57ab49c27ff7fa8d05.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO contact_push_keys(contact_id, key_id, key, created_at)\n VALUES (?, ?, ?, ?)\n ON CONFLICT(contact_id, key_id)\n DO UPDATE SET key = excluded.key, created_at = excluded.created_at\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 4
- },
- "nullable": []
- },
- "hash": "b375c5808bf4526a65d96c6262cf03bdb10c4c2dd00a4a57ab49c27ff7fa8d05"
-}
diff --git a/rust/.sqlx/query-b5be3159caa3c6bd1d6408e20faac6b1a334bf987f57b1bddf355498ccd0a1e8.json b/rust/.sqlx/query-b5be3159caa3c6bd1d6408e20faac6b1a334bf987f57b1bddf355498ccd0a1e8.json
new file mode 100644
index 00000000..67578244
--- /dev/null
+++ b/rust/.sqlx/query-b5be3159caa3c6bd1d6408e20faac6b1a334bf987f57b1bddf355498ccd0a1e8.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET recovery_secret_share = ? WHERE user_id = 8",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "b5be3159caa3c6bd1d6408e20faac6b1a334bf987f57b1bddf355498ccd0a1e8"
+}
diff --git a/rust/.sqlx/query-b84d4a6bba4e8eb084bbb6b6ffdff05df10bc93c4ff9bfba248f376439e268a1.json b/rust/.sqlx/query-b84d4a6bba4e8eb084bbb6b6ffdff05df10bc93c4ff9bfba248f376439e268a1.json
new file mode 100644
index 00000000..1df89b90
--- /dev/null
+++ b/rust/.sqlx/query-b84d4a6bba4e8eb084bbb6b6ffdff05df10bc93c4ff9bfba248f376439e268a1.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT media_id FROM media_files WHERE download_state = 'pending'",
+ "describe": {
+ "columns": [
+ {
+ "name": "media_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "media_files",
+ "name": "media_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "b84d4a6bba4e8eb084bbb6b6ffdff05df10bc93c4ff9bfba248f376439e268a1"
+}
diff --git a/rust/.sqlx/query-b88ca6e0e8412660ff6f7be96cf25bfa2f77cfca7578ddcc6a7d7a2d16a88402.json b/rust/.sqlx/query-b88ca6e0e8412660ff6f7be96cf25bfa2f77cfca7578ddcc6a7d7a2d16a88402.json
new file mode 100644
index 00000000..cac2585a
--- /dev/null
+++ b/rust/.sqlx/query-b88ca6e0e8412660ff6f7be96cf25bfa2f77cfca7578ddcc6a7d7a2d16a88402.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM key_verifications WHERE contact_id = ? AND type = 'contactSharedByVerified' AND verified_by = ?)",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM key_verifications WHERE contact_id = ? AND type = 'contactSharedByVerified' AND verified_by = ?)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "b88ca6e0e8412660ff6f7be96cf25bfa2f77cfca7578ddcc6a7d7a2d16a88402"
+}
diff --git a/rust/.sqlx/query-b9ac2c9ad836219b0851fa2acb5cf28bbec02249744c104086ce643b4e5fc6b4.json b/rust/.sqlx/query-b9ac2c9ad836219b0851fa2acb5cf28bbec02249744c104086ce643b4e5fc6b4.json
new file mode 100644
index 00000000..3414bed6
--- /dev/null
+++ b/rust/.sqlx/query-b9ac2c9ad836219b0851fa2acb5cf28bbec02249744c104086ce643b4e5fc6b4.json
@@ -0,0 +1,74 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT accepted, blocked, media_send_counter, user_discovery_excluded,\n user_discovery_manual_approved\n FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "accepted",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "accepted"
+ }
+ }
+ },
+ {
+ "name": "blocked",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "blocked"
+ }
+ }
+ },
+ {
+ "name": "media_send_counter",
+ "ordinal": 2,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "media_send_counter"
+ }
+ }
+ },
+ {
+ "name": "user_discovery_excluded",
+ "ordinal": 3,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_discovery_excluded"
+ }
+ }
+ },
+ {
+ "name": "user_discovery_manual_approved",
+ "ordinal": 4,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_discovery_manual_approved"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ true
+ ]
+ },
+ "hash": "b9ac2c9ad836219b0851fa2acb5cf28bbec02249744c104086ce643b4e5fc6b4"
+}
diff --git a/rust/.sqlx/query-b9b7b629a1b669c5bcabf97209e8eb6239fc973bad00068cf15022f1adc28c19.json b/rust/.sqlx/query-b9b7b629a1b669c5bcabf97209e8eb6239fc973bad00068cf15022f1adc28c19.json
new file mode 100644
index 00000000..8b4fbefd
--- /dev/null
+++ b/rust/.sqlx/query-b9b7b629a1b669c5bcabf97209e8eb6239fc973bad00068cf15022f1adc28c19.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE groups\n SET also_best_friend = ?,\n flame_counter = ?,\n max_flame_counter = ?\n WHERE group_id = ?\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 4
+ },
+ "nullable": []
+ },
+ "hash": "b9b7b629a1b669c5bcabf97209e8eb6239fc973bad00068cf15022f1adc28c19"
+}
diff --git a/rust/.sqlx/query-b9d176b15d5c2a7b8c03eb6975b7faf251ff3f318e0f7cb138131eaa705d267b.json b/rust/.sqlx/query-b9d176b15d5c2a7b8c03eb6975b7faf251ff3f318e0f7cb138131eaa705d267b.json
new file mode 100644
index 00000000..1e43d5e3
--- /dev/null
+++ b/rust/.sqlx/query-b9d176b15d5c2a7b8c03eb6975b7faf251ff3f318e0f7cb138131eaa705d267b.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM signal_sessions WHERE name = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "b9d176b15d5c2a7b8c03eb6975b7faf251ff3f318e0f7cb138131eaa705d267b"
+}
diff --git a/rust/.sqlx/query-bc0a466a7b9cbdf21d3434b9fb6320ce1184e38b26d8074dbef3173173fee740.json b/rust/.sqlx/query-bc0a466a7b9cbdf21d3434b9fb6320ce1184e38b26d8074dbef3173173fee740.json
new file mode 100644
index 00000000..fcc00887
--- /dev/null
+++ b/rust/.sqlx/query-bc0a466a7b9cbdf21d3434b9fb6320ce1184e38b26d8074dbef3173173fee740.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT INTO groups(group_id, group_name, is_direct_chat, is_group_admin, joined_group)\n VALUES (?, ?, 1, 1, 1)\n ON CONFLICT(group_id) DO UPDATE SET joined_group = 1\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "bc0a466a7b9cbdf21d3434b9fb6320ce1184e38b26d8074dbef3173173fee740"
+}
diff --git a/rust/.sqlx/query-bc4170238e7463dbfd973883ba4f3b3a5f1fc6954c1d26adb1e48ca80de2c7f8.json b/rust/.sqlx/query-bc4170238e7463dbfd973883ba4f3b3a5f1fc6954c1d26adb1e48ca80de2c7f8.json
new file mode 100644
index 00000000..8fe39d59
--- /dev/null
+++ b/rust/.sqlx/query-bc4170238e7463dbfd973883ba4f3b3a5f1fc6954c1d26adb1e48ca80de2c7f8.json
@@ -0,0 +1,62 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT sender_id, content, quotes_message_id, is_deleted_from_sender FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "sender_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "sender_id"
+ }
+ }
+ },
+ {
+ "name": "content",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "content"
+ }
+ }
+ },
+ {
+ "name": "quotes_message_id",
+ "ordinal": 2,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "quotes_message_id"
+ }
+ }
+ },
+ {
+ "name": "is_deleted_from_sender",
+ "ordinal": 3,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "is_deleted_from_sender"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true,
+ true,
+ true,
+ false
+ ]
+ },
+ "hash": "bc4170238e7463dbfd973883ba4f3b3a5f1fc6954c1d26adb1e48ca80de2c7f8"
+}
diff --git a/rust/.sqlx/query-0c7011264675a005d09dbe0f690e9475f8b8964a8cbce2b0dd4919bd5953bc08.json b/rust/.sqlx/query-bc787f9691dde78e262af844b775841212afe9f4de3d3f0709ba1c1f76726ae8.json
similarity index 55%
rename from rust/.sqlx/query-0c7011264675a005d09dbe0f690e9475f8b8964a8cbce2b0dd4919bd5953bc08.json
rename to rust/.sqlx/query-bc787f9691dde78e262af844b775841212afe9f4de3d3f0709ba1c1f76726ae8.json
index b20434ce..32f1aa46 100644
--- a/rust/.sqlx/query-0c7011264675a005d09dbe0f690e9475f8b8964a8cbce2b0dd4919bd5953bc08.json
+++ b/rust/.sqlx/query-bc787f9691dde78e262af844b775841212afe9f4de3d3f0709ba1c1f76726ae8.json
@@ -1,16 +1,16 @@
{
"db_name": "SQLite",
- "query": "\n SELECT content\n FROM messages\n WHERE message_id = ?\n ",
+ "query": "SELECT media_id FROM messages WHERE message_id = ?",
"describe": {
"columns": [
{
- "name": "content",
+ "name": "media_id",
"ordinal": 0,
"type_info": "Text",
"origin": {
"Table": {
"table": "messages",
- "name": "content"
+ "name": "media_id"
}
}
}
@@ -22,5 +22,5 @@
true
]
},
- "hash": "0c7011264675a005d09dbe0f690e9475f8b8964a8cbce2b0dd4919bd5953bc08"
+ "hash": "bc787f9691dde78e262af844b775841212afe9f4de3d3f0709ba1c1f76726ae8"
}
diff --git a/rust/.sqlx/query-bc9cdc4b65da8ee1c8c40a72dfd44f5b5c8016a582c36571ce656764b8f2b5fd.json b/rust/.sqlx/query-bc9cdc4b65da8ee1c8c40a72dfd44f5b5c8016a582c36571ce656764b8f2b5fd.json
new file mode 100644
index 00000000..9f15095d
--- /dev/null
+++ b/rust/.sqlx/query-bc9cdc4b65da8ee1c8c40a72dfd44f5b5c8016a582c36571ce656764b8f2b5fd.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE groups SET last_message_exchange = CAST(strftime('%s','now') AS INTEGER) WHERE group_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "bc9cdc4b65da8ee1c8c40a72dfd44f5b5c8016a582c36571ce656764b8f2b5fd"
+}
diff --git a/rust/.sqlx/query-08ec1e1198b16f3bb313b4c2c5a92bd8ca3251f0aac8d8e2748a7617c53afc65.json b/rust/.sqlx/query-bd2ac211ab56544bc1eeb1a3d47411b577e5d7a4ebb213743bb174ebdacdb30c.json
similarity index 51%
rename from rust/.sqlx/query-08ec1e1198b16f3bb313b4c2c5a92bd8ca3251f0aac8d8e2748a7617c53afc65.json
rename to rust/.sqlx/query-bd2ac211ab56544bc1eeb1a3d47411b577e5d7a4ebb213743bb174ebdacdb30c.json
index d9052206..e1919040 100644
--- a/rust/.sqlx/query-08ec1e1198b16f3bb313b4c2c5a92bd8ca3251f0aac8d8e2748a7617c53afc65.json
+++ b/rust/.sqlx/query-bd2ac211ab56544bc1eeb1a3d47411b577e5d7a4ebb213743bb174ebdacdb30c.json
@@ -1,16 +1,16 @@
{
"db_name": "SQLite",
- "query": "\n SELECT identity_key\n FROM signal_identities\n WHERE name = ?\n ",
+ "query": "SELECT opened_at FROM messages WHERE message_id = ?",
"describe": {
"columns": [
{
- "name": "identity_key",
+ "name": "opened_at",
"ordinal": 0,
- "type_info": "Blob",
+ "type_info": "Integer",
"origin": {
"Table": {
- "table": "signal_identities",
- "name": "identity_key"
+ "table": "messages",
+ "name": "opened_at"
}
}
}
@@ -19,8 +19,8 @@
"Right": 1
},
"nullable": [
- false
+ true
]
},
- "hash": "08ec1e1198b16f3bb313b4c2c5a92bd8ca3251f0aac8d8e2748a7617c53afc65"
+ "hash": "bd2ac211ab56544bc1eeb1a3d47411b577e5d7a4ebb213743bb174ebdacdb30c"
}
diff --git a/rust/.sqlx/query-bdf69a9fec2ffc402c655567c8b29eb77ab35a1dcaf3c1f129b83c331da33329.json b/rust/.sqlx/query-bdf69a9fec2ffc402c655567c8b29eb77ab35a1dcaf3c1f129b83c331da33329.json
new file mode 100644
index 00000000..dd59eec8
--- /dev/null
+++ b/rust/.sqlx/query-bdf69a9fec2ffc402c655567c8b29eb77ab35a1dcaf3c1f129b83c331da33329.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "\n SELECT receipt_id FROM receipts\n WHERE will_be_retried_by_media_upload = 0\n AND (ack_by_server_at IS NULL OR mark_for_retry IS NOT NULL)\n AND (mark_for_retry_after_accepted IS NULL OR EXISTS(\n SELECT 1 FROM contacts\n WHERE contacts.user_id = receipts.contact_id AND contacts.accepted = 1\n ))\n ORDER BY created_at\n ",
+ "describe": {
+ "columns": [
+ {
+ "name": "receipt_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "receipts",
+ "name": "receipt_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "bdf69a9fec2ffc402c655567c8b29eb77ab35a1dcaf3c1f129b83c331da33329"
+}
diff --git a/rust/.sqlx/query-be0c18b2b1c60c720364458799336262f49d3cf2473f7d7305c81047d0cc7aa5.json b/rust/.sqlx/query-be0c18b2b1c60c720364458799336262f49d3cf2473f7d7305c81047d0cc7aa5.json
new file mode 100644
index 00000000..07f1a1bb
--- /dev/null
+++ b/rust/.sqlx/query-be0c18b2b1c60c720364458799336262f49d3cf2473f7d7305c81047d0cc7aa5.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE contacts SET\n username = COALESCE(?, username),\n display_name = CASE WHEN ? THEN ? ELSE display_name END,\n avatar_svg_compressed = CASE WHEN ? THEN ? ELSE avatar_svg_compressed END,\n sender_profile_counter = COALESCE(?, sender_profile_counter),\n signal_version = COALESCE(?, signal_version),\n accepted = COALESCE(?, accepted),\n requested = COALESCE(?, requested),\n deleted_by_user = COALESCE(?, deleted_by_user),\n blocked = COALESCE(?, blocked)\n WHERE user_id = ? AND (? = 0 OR requested = 0)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 13
+ },
+ "nullable": []
+ },
+ "hash": "be0c18b2b1c60c720364458799336262f49d3cf2473f7d7305c81047d0cc7aa5"
+}
diff --git a/rust/.sqlx/query-be136b085a392787e934ac0026f3621d0f6208e4d786e6ac9d1dc16b5dbdd238.json b/rust/.sqlx/query-be136b085a392787e934ac0026f3621d0f6208e4d786e6ac9d1dc16b5dbdd238.json
new file mode 100644
index 00000000..b4569d3b
--- /dev/null
+++ b/rust/.sqlx/query-be136b085a392787e934ac0026f3621d0f6208e4d786e6ac9d1dc16b5dbdd238.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT user_id FROM contacts WHERE username IN ('[deleted]', '[Unknown]')",
+ "describe": {
+ "columns": [
+ {
+ "name": "user_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "be136b085a392787e934ac0026f3621d0f6208e4d786e6ac9d1dc16b5dbdd238"
+}
diff --git a/rust/.sqlx/query-beef80b471a50234ac99decd9bef164fceb680c1b53760fc5b0e83a8216be53d.json b/rust/.sqlx/query-beef80b471a50234ac99decd9bef164fceb680c1b53760fc5b0e83a8216be53d.json
new file mode 100644
index 00000000..e52f0860
--- /dev/null
+++ b/rust/.sqlx/query-beef80b471a50234ac99decd9bef164fceb680c1b53760fc5b0e83a8216be53d.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM user_discovery_announced_users WHERE announced_user_id = ?)",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM user_discovery_announced_users WHERE announced_user_id = ?)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "beef80b471a50234ac99decd9bef164fceb680c1b53760fc5b0e83a8216be53d"
+}
diff --git a/rust/.sqlx/query-fdc73a53a58eea5df4925e2a11ce1befa52f1dc0f709de124d9c6f5ec29c88f0.json b/rust/.sqlx/query-bf32b99b2e2332ce9903bd6c41540b365a60565bcdd6e75a82fbdf64ad031493.json
similarity index 53%
rename from rust/.sqlx/query-fdc73a53a58eea5df4925e2a11ce1befa52f1dc0f709de124d9c6f5ec29c88f0.json
rename to rust/.sqlx/query-bf32b99b2e2332ce9903bd6c41540b365a60565bcdd6e75a82fbdf64ad031493.json
index 3b2afdea..7e9c2cfe 100644
--- a/rust/.sqlx/query-fdc73a53a58eea5df4925e2a11ce1befa52f1dc0f709de124d9c6f5ec29c88f0.json
+++ b/rust/.sqlx/query-bf32b99b2e2332ce9903bd6c41540b365a60565bcdd6e75a82fbdf64ad031493.json
@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
- "query": "\n INSERT INTO received_receipts(receipt_id)\n VALUES (?)\n ",
+ "query": "\n INSERT INTO received_receipts(receipt_id)\n VALUES (?)\n ON CONFLICT(receipt_id) DO NOTHING\n ",
"describe": {
"columns": [],
"parameters": {
@@ -8,5 +8,5 @@
},
"nullable": []
},
- "hash": "fdc73a53a58eea5df4925e2a11ce1befa52f1dc0f709de124d9c6f5ec29c88f0"
+ "hash": "bf32b99b2e2332ce9903bd6c41540b365a60565bcdd6e75a82fbdf64ad031493"
}
diff --git a/rust/.sqlx/query-c1993cbf0d0255b2e68d0bf4ffc80d37fdec7b2d995b2553e534e9dc6e6db195.json b/rust/.sqlx/query-c1993cbf0d0255b2e68d0bf4ffc80d37fdec7b2d995b2553e534e9dc6e6db195.json
deleted file mode 100644
index 2b400771..00000000
--- a/rust/.sqlx/query-c1993cbf0d0255b2e68d0bf4ffc80d37fdec7b2d995b2553e534e9dc6e6db195.json
+++ /dev/null
@@ -1,47 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n SELECT\n (SELECT COUNT(*) FROM messages WHERE message_id LIKE 'path-media-%') AS media_count,\n (SELECT COUNT(*) FROM group_histories WHERE group_id = ?) AS history_count,\n (SELECT COUNT(*) FROM contact_push_keys WHERE contact_id = ?) AS push_key_count,\n (SELECT ask_for_friend_promotions FROM contacts WHERE user_id = ?) AS ask_promotions\n ",
- "describe": {
- "columns": [
- {
- "name": "media_count",
- "ordinal": 0,
- "type_info": "Integer",
- "origin": "Expression"
- },
- {
- "name": "history_count",
- "ordinal": 1,
- "type_info": "Integer",
- "origin": "Expression"
- },
- {
- "name": "push_key_count",
- "ordinal": 2,
- "type_info": "Integer",
- "origin": "Expression"
- },
- {
- "name": "ask_promotions",
- "ordinal": 3,
- "type_info": "Integer",
- "origin": {
- "Table": {
- "table": "contacts",
- "name": "ask_for_friend_promotions"
- }
- }
- }
- ],
- "parameters": {
- "Right": 3
- },
- "nullable": [
- false,
- false,
- false,
- true
- ]
- },
- "hash": "c1993cbf0d0255b2e68d0bf4ffc80d37fdec7b2d995b2553e534e9dc6e6db195"
-}
diff --git a/rust/.sqlx/query-c471755c5d39658b62ead063f040573ab36022c3f7c3cc9d039caafc45f49616.json b/rust/.sqlx/query-c471755c5d39658b62ead063f040573ab36022c3f7c3cc9d039caafc45f49616.json
new file mode 100644
index 00000000..ebecc648
--- /dev/null
+++ b/rust/.sqlx/query-c471755c5d39658b62ead063f040573ab36022c3f7c3cc9d039caafc45f49616.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT is_deleted_from_sender FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "is_deleted_from_sender",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "is_deleted_from_sender"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "c471755c5d39658b62ead063f040573ab36022c3f7c3cc9d039caafc45f49616"
+}
diff --git a/rust/.sqlx/query-c4845dbfc70e136ed2ccbb9efa78a9bdba7773c4f5ab2276da9b9ac1e74693dd.json b/rust/.sqlx/query-c4845dbfc70e136ed2ccbb9efa78a9bdba7773c4f5ab2276da9b9ac1e74693dd.json
new file mode 100644
index 00000000..832bb6de
--- /dev/null
+++ b/rust/.sqlx/query-c4845dbfc70e136ed2ccbb9efa78a9bdba7773c4f5ab2276da9b9ac1e74693dd.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(DISTINCT from_contact_id) FROM user_discovery_user_relations WHERE announced_user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "COUNT(DISTINCT from_contact_id)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "c4845dbfc70e136ed2ccbb9efa78a9bdba7773c4f5ab2276da9b9ac1e74693dd"
+}
diff --git a/rust/.sqlx/query-c56b5839278ea567fdde83485ddb42baa0780992501f9e836794a21d2e2e3b58.json b/rust/.sqlx/query-c56b5839278ea567fdde83485ddb42baa0780992501f9e836794a21d2e2e3b58.json
new file mode 100644
index 00000000..fd719202
--- /dev/null
+++ b/rust/.sqlx/query-c56b5839278ea567fdde83485ddb42baa0780992501f9e836794a21d2e2e3b58.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "c56b5839278ea567fdde83485ddb42baa0780992501f9e836794a21d2e2e3b58"
+}
diff --git a/rust/.sqlx/query-c57017d2c9c2b4b2282f89010ddd4250b566cda50870d09ecedc7e8eb81c5c26.json b/rust/.sqlx/query-c57017d2c9c2b4b2282f89010ddd4250b566cda50870d09ecedc7e8eb81c5c26.json
deleted file mode 100644
index 77edd100..00000000
--- a/rust/.sqlx/query-c57017d2c9c2b4b2282f89010ddd4250b566cda50870d09ecedc7e8eb81c5c26.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n SELECT SUM(retry_count)\n FROM receipts\n WHERE receipt_id LIKE 'path-plaintext-%'\n ",
- "describe": {
- "columns": [
- {
- "name": "SUM(retry_count)",
- "ordinal": 0,
- "type_info": "Integer",
- "origin": "Expression"
- }
- ],
- "parameters": {
- "Right": 0
- },
- "nullable": [
- true
- ]
- },
- "hash": "c57017d2c9c2b4b2282f89010ddd4250b566cda50870d09ecedc7e8eb81c5c26"
-}
diff --git a/rust/.sqlx/query-c618a305e1355fcefa5c8420544779b5fe4f003a16ba01ee2834f028fabf213f.json b/rust/.sqlx/query-c618a305e1355fcefa5c8420544779b5fe4f003a16ba01ee2834f028fabf213f.json
new file mode 100644
index 00000000..4f2971be
--- /dev/null
+++ b/rust/.sqlx/query-c618a305e1355fcefa5c8420544779b5fe4f003a16ba01ee2834f028fabf213f.json
@@ -0,0 +1,326 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT * FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "user_id",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_id"
+ }
+ }
+ },
+ {
+ "name": "username",
+ "ordinal": 1,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "username"
+ }
+ }
+ },
+ {
+ "name": "display_name",
+ "ordinal": 2,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "display_name"
+ }
+ }
+ },
+ {
+ "name": "nick_name",
+ "ordinal": 3,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "nick_name"
+ }
+ }
+ },
+ {
+ "name": "avatar_svg_compressed",
+ "ordinal": 4,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "avatar_svg_compressed"
+ }
+ }
+ },
+ {
+ "name": "sender_profile_counter",
+ "ordinal": 5,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "sender_profile_counter"
+ }
+ }
+ },
+ {
+ "name": "accepted",
+ "ordinal": 6,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "accepted"
+ }
+ }
+ },
+ {
+ "name": "deleted_by_user",
+ "ordinal": 7,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "deleted_by_user"
+ }
+ }
+ },
+ {
+ "name": "requested",
+ "ordinal": 8,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "requested"
+ }
+ }
+ },
+ {
+ "name": "blocked",
+ "ordinal": 9,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "blocked"
+ }
+ }
+ },
+ {
+ "name": "verified",
+ "ordinal": 10,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "verified"
+ }
+ }
+ },
+ {
+ "name": "account_deleted",
+ "ordinal": 11,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "account_deleted"
+ }
+ }
+ },
+ {
+ "name": "created_at",
+ "ordinal": 12,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "created_at"
+ }
+ }
+ },
+ {
+ "name": "signal_version",
+ "ordinal": 13,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "signal_version"
+ }
+ }
+ },
+ {
+ "name": "user_discovery_version",
+ "ordinal": 14,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_discovery_version"
+ }
+ }
+ },
+ {
+ "name": "user_discovery_excluded",
+ "ordinal": 15,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_discovery_excluded"
+ }
+ }
+ },
+ {
+ "name": "user_discovery_manual_approved",
+ "ordinal": 16,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "user_discovery_manual_approved"
+ }
+ }
+ },
+ {
+ "name": "recovery_is_trusted_friend",
+ "ordinal": 17,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_is_trusted_friend"
+ }
+ }
+ },
+ {
+ "name": "recovery_last_heartbeat",
+ "ordinal": 18,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_last_heartbeat"
+ }
+ }
+ },
+ {
+ "name": "recovery_secret_share",
+ "ordinal": 19,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_secret_share"
+ }
+ }
+ },
+ {
+ "name": "recovery_contacts_secret_share",
+ "ordinal": 20,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_contacts_secret_share"
+ }
+ }
+ },
+ {
+ "name": "recovery_contacts_last_heartbeat",
+ "ordinal": 21,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_contacts_last_heartbeat"
+ }
+ }
+ },
+ {
+ "name": "recovery_contacts_threshold",
+ "ordinal": 22,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_contacts_threshold"
+ }
+ }
+ },
+ {
+ "name": "ask_for_friend_promotions",
+ "ordinal": 23,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "ask_for_friend_promotions"
+ }
+ }
+ },
+ {
+ "name": "media_send_counter",
+ "ordinal": 24,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "media_send_counter"
+ }
+ }
+ },
+ {
+ "name": "media_received_counter",
+ "ordinal": 25,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "media_received_counter"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false,
+ true,
+ true,
+ true,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ true,
+ false,
+ true,
+ false,
+ true,
+ true,
+ true,
+ true,
+ true,
+ true,
+ false,
+ false
+ ]
+ },
+ "hash": "c618a305e1355fcefa5c8420544779b5fe4f003a16ba01ee2834f028fabf213f"
+}
diff --git a/rust/.sqlx/query-c6818f3b395db59a7fbe80435c4ef89416ff0cfb32139abff226eec319053a68.json b/rust/.sqlx/query-c6818f3b395db59a7fbe80435c4ef89416ff0cfb32139abff226eec319053a68.json
new file mode 100644
index 00000000..7f8c2fe2
--- /dev/null
+++ b/rust/.sqlx/query-c6818f3b395db59a7fbe80435c4ef89416ff0cfb32139abff226eec319053a68.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT INTO messages(group_id, message_id, type, content, quotes_message_id, created_at)\n VALUES (?, ?, 'text', ?, ?, ?)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 5
+ },
+ "nullable": []
+ },
+ "hash": "c6818f3b395db59a7fbe80435c4ef89416ff0cfb32139abff226eec319053a68"
+}
diff --git a/rust/.sqlx/query-c6878c3532b6555c1e1a51d6602ba74141445777e0b9cbd882a875d5c3481fca.json b/rust/.sqlx/query-c6878c3532b6555c1e1a51d6602ba74141445777e0b9cbd882a875d5c3481fca.json
new file mode 100644
index 00000000..c5462cf0
--- /dev/null
+++ b/rust/.sqlx/query-c6878c3532b6555c1e1a51d6602ba74141445777e0b9cbd882a875d5c3481fca.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE user_discovery_own_promotions SET promotion = X'' WHERE contact_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "c6878c3532b6555c1e1a51d6602ba74141445777e0b9cbd882a875d5c3481fca"
+}
diff --git a/rust/.sqlx/query-c7ad38ad8d6d726a67bf800b075b0496711be766ef54633d0d5a2dff6b5a57cc.json b/rust/.sqlx/query-c7ad38ad8d6d726a67bf800b075b0496711be766ef54633d0d5a2dff6b5a57cc.json
new file mode 100644
index 00000000..5fa7bd7e
--- /dev/null
+++ b/rust/.sqlx/query-c7ad38ad8d6d726a67bf800b075b0496711be766ef54633d0d5a2dff6b5a57cc.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM contacts WHERE user_id = ? AND accepted = 1\n AND blocked = 0 AND media_send_counter >= ? AND user_discovery_excluded = 0\n AND (? = 0 OR user_discovery_manual_approved = 1))",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM contacts WHERE user_id = ? AND accepted = 1\n AND blocked = 0 AND media_send_counter >= ? AND user_discovery_excluded = 0\n AND (? = 0 OR user_discovery_manual_approved = 1))",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "c7ad38ad8d6d726a67bf800b075b0496711be766ef54633d0d5a2dff6b5a57cc"
+}
diff --git a/rust/.sqlx/query-c92d114f6a87655124f8bbdf8df3564966e6ab55725b417f44d353279627dac9.json b/rust/.sqlx/query-c92d114f6a87655124f8bbdf8df3564966e6ab55725b417f44d353279627dac9.json
new file mode 100644
index 00000000..cb2fbaa4
--- /dev/null
+++ b/rust/.sqlx/query-c92d114f6a87655124f8bbdf8df3564966e6ab55725b417f44d353279627dac9.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE user_discovery_announced_users SET username = ? WHERE announced_user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "c92d114f6a87655124f8bbdf8df3564966e6ab55725b417f44d353279627dac9"
+}
diff --git a/rust/.sqlx/query-cd9f690447bdbe9dc52534194400781076ed1d3ca59dd52030c892dc4dc15224.json b/rust/.sqlx/query-cd9f690447bdbe9dc52534194400781076ed1d3ca59dd52030c892dc4dc15224.json
deleted file mode 100644
index 4b3f0fe0..00000000
--- a/rust/.sqlx/query-cd9f690447bdbe9dc52534194400781076ed1d3ca59dd52030c892dc4dc15224.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE contacts\n SET requested = 0, accepted = 0, deleted_by_user = 1\n WHERE user_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 1
- },
- "nullable": []
- },
- "hash": "cd9f690447bdbe9dc52534194400781076ed1d3ca59dd52030c892dc4dc15224"
-}
diff --git a/rust/.sqlx/query-ced6f5964217225fb749bde7641da1f7bf365a53a4d06aa2696a1d24c2b398f7.json b/rust/.sqlx/query-ced6f5964217225fb749bde7641da1f7bf365a53a4d06aa2696a1d24c2b398f7.json
deleted file mode 100644
index b704397d..00000000
--- a/rust/.sqlx/query-ced6f5964217225fb749bde7641da1f7bf365a53a4d06aa2696a1d24c2b398f7.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO user_discovery_other_promotions (\n from_contact_id,\n promotion_id,\n public_id,\n threshold,\n announcement_share,\n public_key_verified_timestamp\n ) VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(from_contact_id, public_id) DO UPDATE SET\n promotion_id = excluded.promotion_id,\n threshold = excluded.threshold,\n announcement_share = excluded.announcement_share,\n public_key_verified_timestamp = excluded.public_key_verified_timestamp\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 6
- },
- "nullable": []
- },
- "hash": "ced6f5964217225fb749bde7641da1f7bf365a53a4d06aa2696a1d24c2b398f7"
-}
diff --git a/rust/.sqlx/query-cf821c9b1530db447a1ba030c3be50c5eeeb78cb28fe74481d6493a6a0af4161.json b/rust/.sqlx/query-cf821c9b1530db447a1ba030c3be50c5eeeb78cb28fe74481d6493a6a0af4161.json
new file mode 100644
index 00000000..3251c7d7
--- /dev/null
+++ b/rust/.sqlx/query-cf821c9b1530db447a1ba030c3be50c5eeeb78cb28fe74481d6493a6a0af4161.json
@@ -0,0 +1,28 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(*) AS \"count!: i64\", MAX(last_retry) AS last_retry\n FROM receipts WHERE contact_id = ? AND message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "count!: i64",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ },
+ {
+ "name": "last_retry",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ false,
+ true
+ ]
+ },
+ "hash": "cf821c9b1530db447a1ba030c3be50c5eeeb78cb28fe74481d6493a6a0af4161"
+}
diff --git a/rust/.sqlx/query-d1b24b806f4071108ba198504845cfeeeddc63628ed9ace6db3107ab2a4262ec.json b/rust/.sqlx/query-d1b24b806f4071108ba198504845cfeeeddc63628ed9ace6db3107ab2a4262ec.json
deleted file mode 100644
index 2d8555db..00000000
--- a/rust/.sqlx/query-d1b24b806f4071108ba198504845cfeeeddc63628ed9ace6db3107ab2a4262ec.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE messages\n SET content = ?, modified_at = ?\n WHERE message_id = ? AND sender_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 4
- },
- "nullable": []
- },
- "hash": "d1b24b806f4071108ba198504845cfeeeddc63628ed9ace6db3107ab2a4262ec"
-}
diff --git a/rust/.sqlx/query-d2688abdeef4590a1c762a1683a638a25806d9be376027c4ac86955a42135ef2.json b/rust/.sqlx/query-d2688abdeef4590a1c762a1683a638a25806d9be376027c4ac86955a42135ef2.json
new file mode 100644
index 00000000..0a1da0c1
--- /dev/null
+++ b/rust/.sqlx/query-d2688abdeef4590a1c762a1683a638a25806d9be376027c4ac86955a42135ef2.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE receipts\n SET receipt_id = ?,\n mark_for_retry = CAST(strftime('%s', 'now') AS INTEGER),\n retry_count = retry_count + 1,\n ack_by_server_at = NULL\n WHERE receipt_id = ? AND contact_id = ?\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "d2688abdeef4590a1c762a1683a638a25806d9be376027c4ac86955a42135ef2"
+}
diff --git a/rust/.sqlx/query-d3598ec91afa76d18c5da4408bf49a16ab2caafd89bd68c58e04417921d039a6.json b/rust/.sqlx/query-d3598ec91afa76d18c5da4408bf49a16ab2caafd89bd68c58e04417921d039a6.json
new file mode 100644
index 00000000..9a1ca20f
--- /dev/null
+++ b/rust/.sqlx/query-d3598ec91afa76d18c5da4408bf49a16ab2caafd89bd68c58e04417921d039a6.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE user_discovery_shares SET contact_id = ? WHERE share_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "d3598ec91afa76d18c5da4408bf49a16ab2caafd89bd68c58e04417921d039a6"
+}
diff --git a/rust/.sqlx/query-d495889cd14181159e65bedf607ec7488e8c04946936dee8eb7175a10b69e77a.json b/rust/.sqlx/query-d495889cd14181159e65bedf607ec7488e8c04946936dee8eb7175a10b69e77a.json
new file mode 100644
index 00000000..c5c50825
--- /dev/null
+++ b/rust/.sqlx/query-d495889cd14181159e65bedf607ec7488e8c04946936dee8eb7175a10b69e77a.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT blocked FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "blocked",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "blocked"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "d495889cd14181159e65bedf607ec7488e8c04946936dee8eb7175a10b69e77a"
+}
diff --git a/rust/.sqlx/query-d7d94080dcd1ae95ed70072c040435db4f1f922e46041d28836e068981df3df6.json b/rust/.sqlx/query-d7d94080dcd1ae95ed70072c040435db4f1f922e46041d28836e068981df3df6.json
new file mode 100644
index 00000000..026301ba
--- /dev/null
+++ b/rust/.sqlx/query-d7d94080dcd1ae95ed70072c040435db4f1f922e46041d28836e068981df3df6.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT username, deleted_by_user FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "username",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "username"
+ }
+ }
+ },
+ {
+ "name": "deleted_by_user",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "deleted_by_user"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ false
+ ]
+ },
+ "hash": "d7d94080dcd1ae95ed70072c040435db4f1f922e46041d28836e068981df3df6"
+}
diff --git a/rust/.sqlx/query-d8a516dd12b7f5d49807b1b5c6970f0e004f9c39a23577473d348ebb6e2b044a.json b/rust/.sqlx/query-d8a516dd12b7f5d49807b1b5c6970f0e004f9c39a23577473d348ebb6e2b044a.json
deleted file mode 100644
index 87a45a41..00000000
--- a/rust/.sqlx/query-d8a516dd12b7f5d49807b1b5c6970f0e004f9c39a23577473d348ebb6e2b044a.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO user_discovery_announced_users (\n announced_user_id,\n announced_public_key,\n public_id\n ) VALUES (?, ?, ?)\n ON CONFLICT DO UPDATE SET\n announced_user_id = excluded.announced_user_id,\n announced_public_key = excluded.announced_public_key,\n public_id = excluded.public_id\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 3
- },
- "nullable": []
- },
- "hash": "d8a516dd12b7f5d49807b1b5c6970f0e004f9c39a23577473d348ebb6e2b044a"
-}
diff --git a/rust/.sqlx/query-d8a8544c1d3eafe0b9c6f0ebd1b7102e792e3d27722294bdc626acde1b46ba2e.json b/rust/.sqlx/query-d8a8544c1d3eafe0b9c6f0ebd1b7102e792e3d27722294bdc626acde1b46ba2e.json
deleted file mode 100644
index 3c1e66fd..00000000
--- a/rust/.sqlx/query-d8a8544c1d3eafe0b9c6f0ebd1b7102e792e3d27722294bdc626acde1b46ba2e.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO contacts(user_id, username, accepted, signal_version)\n VALUES (?, ?, 1, 'v2')\n ON CONFLICT(user_id) DO UPDATE SET\n username = excluded.username,\n accepted = 1,\n signal_version = 'v2'\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "d8a8544c1d3eafe0b9c6f0ebd1b7102e792e3d27722294bdc626acde1b46ba2e"
-}
diff --git a/rust/.sqlx/query-da86a64379fbe00301ccdd82c721b99f3c78465544975d85e9e753337b2fce3e.json b/rust/.sqlx/query-da86a64379fbe00301ccdd82c721b99f3c78465544975d85e9e753337b2fce3e.json
deleted file mode 100644
index 692e7321..00000000
--- a/rust/.sqlx/query-da86a64379fbe00301ccdd82c721b99f3c78465544975d85e9e753337b2fce3e.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO signal_identities (name, identity_key, timestamp)\n VALUES ('1', ?, 0)\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 1
- },
- "nullable": []
- },
- "hash": "da86a64379fbe00301ccdd82c721b99f3c78465544975d85e9e753337b2fce3e"
-}
diff --git a/rust/.sqlx/query-da9609bbcd1676a863f4612f19a24cfdf518d18dca57ba1693b0d51345a24ce1.json b/rust/.sqlx/query-da9609bbcd1676a863f4612f19a24cfdf518d18dca57ba1693b0d51345a24ce1.json
deleted file mode 100644
index 09d4437e..00000000
--- a/rust/.sqlx/query-da9609bbcd1676a863f4612f19a24cfdf518d18dca57ba1693b0d51345a24ce1.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO receipts(receipt_id, contact_id, message_id, message)\n VALUES (?, ?, 'path-text-message', x'01')\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "da9609bbcd1676a863f4612f19a24cfdf518d18dca57ba1693b0d51345a24ce1"
-}
diff --git a/rust/.sqlx/query-db998a6faae4968d03efb6496202c07be4b7c353d12372842b6cd5eb80fb7fd3.json b/rust/.sqlx/query-db998a6faae4968d03efb6496202c07be4b7c353d12372842b6cd5eb80fb7fd3.json
deleted file mode 100644
index b0a55748..00000000
--- a/rust/.sqlx/query-db998a6faae4968d03efb6496202c07be4b7c353d12372842b6cd5eb80fb7fd3.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO contacts(user_id, username, accepted)\n VALUES(7, 'alice', 1)\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 0
- },
- "nullable": []
- },
- "hash": "db998a6faae4968d03efb6496202c07be4b7c353d12372842b6cd5eb80fb7fd3"
-}
diff --git a/rust/.sqlx/query-dbc3765b4629863c7de0c8452a0c526f11116b42f5718311d794a105548f2909.json b/rust/.sqlx/query-dbc3765b4629863c7de0c8452a0c526f11116b42f5718311d794a105548f2909.json
new file mode 100644
index 00000000..3a6c7c43
--- /dev/null
+++ b/rust/.sqlx/query-dbc3765b4629863c7de0c8452a0c526f11116b42f5718311d794a105548f2909.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT avatar_svg_compressed FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "avatar_svg_compressed",
+ "ordinal": 0,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "avatar_svg_compressed"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "dbc3765b4629863c7de0c8452a0c526f11116b42f5718311d794a105548f2909"
+}
diff --git a/rust/.sqlx/query-dc7fa5581ec738bbfa3b55c5faa28141f6dd07e58b30422ff038d80a10fd08b3.json b/rust/.sqlx/query-dc7fa5581ec738bbfa3b55c5faa28141f6dd07e58b30422ff038d80a10fd08b3.json
new file mode 100644
index 00000000..c6ea2f0a
--- /dev/null
+++ b/rust/.sqlx/query-dc7fa5581ec738bbfa3b55c5faa28141f6dd07e58b30422ff038d80a10fd08b3.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE media_files SET download_state = 'downloading'\n WHERE media_id = ? AND download_state = 'pending'",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "dc7fa5581ec738bbfa3b55c5faa28141f6dd07e58b30422ff038d80a10fd08b3"
+}
diff --git a/rust/.sqlx/query-dfe45f9a925ea0d08a5195335a8d14d257d083543df4c3c8530304f6ba33b09f.json b/rust/.sqlx/query-dfe45f9a925ea0d08a5195335a8d14d257d083543df4c3c8530304f6ba33b09f.json
new file mode 100644
index 00000000..c207d196
--- /dev/null
+++ b/rust/.sqlx/query-dfe45f9a925ea0d08a5195335a8d14d257d083543df4c3c8530304f6ba33b09f.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT group_id FROM groups WHERE joined_group = 0 AND left_group = 0",
+ "describe": {
+ "columns": [
+ {
+ "name": "group_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "groups",
+ "name": "group_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "dfe45f9a925ea0d08a5195335a8d14d257d083543df4c3c8530304f6ba33b09f"
+}
diff --git a/rust/.sqlx/query-e026e8c5eb30ca2f7c98c0d4e8f9ccdf388bf7e98f4336ada2ac09224461082f.json b/rust/.sqlx/query-e026e8c5eb30ca2f7c98c0d4e8f9ccdf388bf7e98f4336ada2ac09224461082f.json
deleted file mode 100644
index e7fdef1c..00000000
--- a/rust/.sqlx/query-e026e8c5eb30ca2f7c98c0d4e8f9ccdf388bf7e98f4336ada2ac09224461082f.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO user_discovery_own_promotions (\n contact_id,\n promotion\n ) VALUES (?, ?)\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "e026e8c5eb30ca2f7c98c0d4e8f9ccdf388bf7e98f4336ada2ac09224461082f"
-}
diff --git a/rust/.sqlx/query-e0af4bff6847c75b26509f029e6a23624c784b5ededae3dcac2b1913cf8221b0.json b/rust/.sqlx/query-e0af4bff6847c75b26509f029e6a23624c784b5ededae3dcac2b1913cf8221b0.json
new file mode 100644
index 00000000..49a18acd
--- /dev/null
+++ b/rust/.sqlx/query-e0af4bff6847c75b26509f029e6a23624c784b5ededae3dcac2b1913cf8221b0.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE messages SET opened_at = ?, opened_by_all = ? WHERE message_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "e0af4bff6847c75b26509f029e6a23624c784b5ededae3dcac2b1913cf8221b0"
+}
diff --git a/rust/.sqlx/query-e215e54ae922900c89f72406e96e16243bdea540c05dea78d1a5d7c1ec12f5df.json b/rust/.sqlx/query-e215e54ae922900c89f72406e96e16243bdea540c05dea78d1a5d7c1ec12f5df.json
new file mode 100644
index 00000000..6ad1217d
--- /dev/null
+++ b/rust/.sqlx/query-e215e54ae922900c89f72406e96e16243bdea540c05dea78d1a5d7c1ec12f5df.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM receipts WHERE message_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "e215e54ae922900c89f72406e96e16243bdea540c05dea78d1a5d7c1ec12f5df"
+}
diff --git a/rust/.sqlx/query-e24b46f465ca104c6f288eafda55b060d389a1a07f54757b0e12ac4d16cc2ec9.json b/rust/.sqlx/query-e24b46f465ca104c6f288eafda55b060d389a1a07f54757b0e12ac4d16cc2ec9.json
new file mode 100644
index 00000000..8c5766b4
--- /dev/null
+++ b/rust/.sqlx/query-e24b46f465ca104c6f288eafda55b060d389a1a07f54757b0e12ac4d16cc2ec9.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE media_files SET download_state = 'ready', stored_file_hash = ?\n WHERE media_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "e24b46f465ca104c6f288eafda55b060d389a1a07f54757b0e12ac4d16cc2ec9"
+}
diff --git a/rust/.sqlx/query-e289178012389d57c467b0bc60f42c5173c81e3afaa26d2f78bc52463963da78.json b/rust/.sqlx/query-e289178012389d57c467b0bc60f42c5173c81e3afaa26d2f78bc52463963da78.json
new file mode 100644
index 00000000..73ad047c
--- /dev/null
+++ b/rust/.sqlx/query-e289178012389d57c467b0bc60f42c5173c81e3afaa26d2f78bc52463963da78.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE receipts\n SET ack_by_server_at = ?, retry_count = 1, last_retry = ?, mark_for_retry = NULL\n WHERE EXISTS(\n SELECT 1\n FROM messages\n JOIN group_members ON group_members.group_id = messages.group_id\n WHERE messages.message_id = receipts.message_id\n AND messages.media_id = ?\n AND group_members.contact_id = receipts.contact_id\n AND (group_members.member_state IS NULL OR group_members.member_state != 'leftGroup')\n )\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "e289178012389d57c467b0bc60f42c5173c81e3afaa26d2f78bc52463963da78"
+}
diff --git a/rust/.sqlx/query-e2a5bdab035e10052f1091991d249407492b87fc3fe4fd56dac858a77a8f6845.json b/rust/.sqlx/query-e2a5bdab035e10052f1091991d249407492b87fc3fe4fd56dac858a77a8f6845.json
deleted file mode 100644
index 18a03b56..00000000
--- a/rust/.sqlx/query-e2a5bdab035e10052f1091991d249407492b87fc3fe4fd56dac858a77a8f6845.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO groups(group_id, group_name)\n VALUES('group', 'Group')\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 0
- },
- "nullable": []
- },
- "hash": "e2a5bdab035e10052f1091991d249407492b87fc3fe4fd56dac858a77a8f6845"
-}
diff --git a/rust/.sqlx/query-e3651626e5a5537a9317a56ac877503ea73bc4446d38221e5100a4ec312e205a.json b/rust/.sqlx/query-e3651626e5a5537a9317a56ac877503ea73bc4446d38221e5100a4ec312e205a.json
new file mode 100644
index 00000000..71b40f6d
--- /dev/null
+++ b/rust/.sqlx/query-e3651626e5a5537a9317a56ac877503ea73bc4446d38221e5100a4ec312e205a.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE receipts\n SET receipt_id = ?,\n mark_for_retry = ?,\n retry_count = retry_count + 1,\n ack_by_server_at = NULL\n WHERE receipt_id = ? AND contact_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 4
+ },
+ "nullable": []
+ },
+ "hash": "e3651626e5a5537a9317a56ac877503ea73bc4446d38221e5100a4ec312e205a"
+}
diff --git a/rust/.sqlx/query-e512dd25f91d718843ac20f97264380991ff99c42097ac021b9db327462d7557.json b/rust/.sqlx/query-e512dd25f91d718843ac20f97264380991ff99c42097ac021b9db327462d7557.json
new file mode 100644
index 00000000..f4693cff
--- /dev/null
+++ b/rust/.sqlx/query-e512dd25f91d718843ac20f97264380991ff99c42097ac021b9db327462d7557.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT OR REPLACE INTO receipts(receipt_id, contact_id, message, contact_will_sends_receipt)\n VALUES (?, ?, ?, 0)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "e512dd25f91d718843ac20f97264380991ff99c42097ac021b9db327462d7557"
+}
diff --git a/rust/.sqlx/query-e5232f66455c97e49ed464ede0943c5c88bb1b6d346711688ed1e34719307988.json b/rust/.sqlx/query-e5232f66455c97e49ed464ede0943c5c88bb1b6d346711688ed1e34719307988.json
new file mode 100644
index 00000000..f40c10fd
--- /dev/null
+++ b/rust/.sqlx/query-e5232f66455c97e49ed464ede0943c5c88bb1b6d346711688ed1e34719307988.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM signal_sessions WHERE name = ? AND device_id = 1)",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM signal_sessions WHERE name = ? AND device_id = 1)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "e5232f66455c97e49ed464ede0943c5c88bb1b6d346711688ed1e34719307988"
+}
diff --git a/rust/.sqlx/query-e569dd744fb4a5e49cfbe14c61005d14f8c0f82a8640269ec29d5e9edabb106b.json b/rust/.sqlx/query-e569dd744fb4a5e49cfbe14c61005d14f8c0f82a8640269ec29d5e9edabb106b.json
new file mode 100644
index 00000000..5d39b48e
--- /dev/null
+++ b/rust/.sqlx/query-e569dd744fb4a5e49cfbe14c61005d14f8c0f82a8640269ec29d5e9edabb106b.json
@@ -0,0 +1,38 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT message_id, sender_id AS \"sender_id!: i64\" FROM messages\n WHERE media_id = ? AND opened_at IS NULL AND sender_id IS NOT NULL",
+ "describe": {
+ "columns": [
+ {
+ "name": "message_id",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "message_id"
+ }
+ }
+ },
+ {
+ "name": "sender_id!: i64",
+ "ordinal": 1,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "sender_id"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false,
+ true
+ ]
+ },
+ "hash": "e569dd744fb4a5e49cfbe14c61005d14f8c0f82a8640269ec29d5e9edabb106b"
+}
diff --git a/rust/.sqlx/query-e89767b813c0619775a9224a1767cfa1dcd44a0bec75f80e4875a097ab5b4491.json b/rust/.sqlx/query-e89767b813c0619775a9224a1767cfa1dcd44a0bec75f80e4875a097ab5b4491.json
new file mode 100644
index 00000000..afb19bfc
--- /dev/null
+++ b/rust/.sqlx/query-e89767b813c0619775a9224a1767cfa1dcd44a0bec75f80e4875a097ab5b4491.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE messages SET ack_by_server = ? WHERE media_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "e89767b813c0619775a9224a1767cfa1dcd44a0bec75f80e4875a097ab5b4491"
+}
diff --git a/rust/.sqlx/query-eb4724c5a03a40f301e5dd8916020032cf706cf6acf70ae69c9a17b7fbaae673.json b/rust/.sqlx/query-eb4724c5a03a40f301e5dd8916020032cf706cf6acf70ae69c9a17b7fbaae673.json
new file mode 100644
index 00000000..55ca98be
--- /dev/null
+++ b/rust/.sqlx/query-eb4724c5a03a40f301e5dd8916020032cf706cf6acf70ae69c9a17b7fbaae673.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE media_files SET upload_state = 'uploaded' WHERE media_id = ? AND upload_state IS NOT 'uploaded'",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "eb4724c5a03a40f301e5dd8916020032cf706cf6acf70ae69c9a17b7fbaae673"
+}
diff --git a/rust/.sqlx/query-eda6535eb2d08a691ac7a032635259b64357b4723a461a719ea7302b5257b130.json b/rust/.sqlx/query-eda6535eb2d08a691ac7a032635259b64357b4723a461a719ea7302b5257b130.json
new file mode 100644
index 00000000..78a361c4
--- /dev/null
+++ b/rust/.sqlx/query-eda6535eb2d08a691ac7a032635259b64357b4723a461a719ea7302b5257b130.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT COUNT(*) FROM user_discovery_user_relations WHERE announced_user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "COUNT(*)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "eda6535eb2d08a691ac7a032635259b64357b4723a461a719ea7302b5257b130"
+}
diff --git a/rust/.sqlx/query-edbc2c8babc15b0c8b73a1d9fd7326cac78c48486de3de8f898d96833828603c.json b/rust/.sqlx/query-edbc2c8babc15b0c8b73a1d9fd7326cac78c48486de3de8f898d96833828603c.json
new file mode 100644
index 00000000..4040dd57
--- /dev/null
+++ b/rust/.sqlx/query-edbc2c8babc15b0c8b73a1d9fd7326cac78c48486de3de8f898d96833828603c.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT media_reopened FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "media_reopened",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "media_reopened"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "edbc2c8babc15b0c8b73a1d9fd7326cac78c48486de3de8f898d96833828603c"
+}
diff --git a/rust/.sqlx/query-eefe20114e1b151c48f308f2f197c9a81dfe0f348725c57f7e01205d68b687a9.json b/rust/.sqlx/query-eefe20114e1b151c48f308f2f197c9a81dfe0f348725c57f7e01205d68b687a9.json
deleted file mode 100644
index 15bb1144..00000000
--- a/rust/.sqlx/query-eefe20114e1b151c48f308f2f197c9a81dfe0f348725c57f7e01205d68b687a9.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE receipts\n SET mark_for_retry = CAST(strftime('%s', 'now') AS INTEGER),\n retry_count = retry_count + 1\n WHERE receipt_id = ? AND contact_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "eefe20114e1b151c48f308f2f197c9a81dfe0f348725c57f7e01205d68b687a9"
-}
diff --git a/rust/.sqlx/query-ef217dcdf24e8161bf27a3a67f22bc4d3e6ad7ea2182686cd9ba9ff1eb9608ef.json b/rust/.sqlx/query-ef217dcdf24e8161bf27a3a67f22bc4d3e6ad7ea2182686cd9ba9ff1eb9608ef.json
new file mode 100644
index 00000000..f6669cbd
--- /dev/null
+++ b/rust/.sqlx/query-ef217dcdf24e8161bf27a3a67f22bc4d3e6ad7ea2182686cd9ba9ff1eb9608ef.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE notification_outbox SET delivered_at = ? WHERE event_id = ? AND delivered_at IS NULL",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "ef217dcdf24e8161bf27a3a67f22bc4d3e6ad7ea2182686cd9ba9ff1eb9608ef"
+}
diff --git a/rust/.sqlx/query-ef8c34f8171dfe13f2c55942f8813fa2e2811ea348375ff8d2864a9d322c6358.json b/rust/.sqlx/query-ef8c34f8171dfe13f2c55942f8813fa2e2811ea348375ff8d2864a9d322c6358.json
new file mode 100644
index 00000000..fdef73bc
--- /dev/null
+++ b/rust/.sqlx/query-ef8c34f8171dfe13f2c55942f8813fa2e2811ea348375ff8d2864a9d322c6358.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM media_files WHERE media_id = ?)",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM media_files WHERE media_id = ?)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "ef8c34f8171dfe13f2c55942f8813fa2e2811ea348375ff8d2864a9d322c6358"
+}
diff --git a/rust/.sqlx/query-f202050b08b772f33d9625797f23bd8c1adb9bd857065911f80d73b625e7801f.json b/rust/.sqlx/query-f202050b08b772f33d9625797f23bd8c1adb9bd857065911f80d73b625e7801f.json
new file mode 100644
index 00000000..7c4cc65a
--- /dev/null
+++ b/rust/.sqlx/query-f202050b08b772f33d9625797f23bd8c1adb9bd857065911f80d73b625e7801f.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE contacts SET signal_version = 'v2' WHERE user_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "f202050b08b772f33d9625797f23bd8c1adb9bd857065911f80d73b625e7801f"
+}
diff --git a/rust/.sqlx/query-f264af9235d61c83056a34a2327949df38ca4ea372b0001c60a31b2896f71cf0.json b/rust/.sqlx/query-f264af9235d61c83056a34a2327949df38ca4ea372b0001c60a31b2896f71cf0.json
new file mode 100644
index 00000000..eda66e77
--- /dev/null
+++ b/rust/.sqlx/query-f264af9235d61c83056a34a2327949df38ca4ea372b0001c60a31b2896f71cf0.json
@@ -0,0 +1,21 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT EXISTS(SELECT 1 FROM user_discovery_shares LIMIT 1)",
+ "describe": {
+ "columns": [
+ {
+ "name": "EXISTS(SELECT 1 FROM user_discovery_shares LIMIT 1)",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": "Expression"
+ }
+ ],
+ "parameters": {
+ "Right": 0
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "f264af9235d61c83056a34a2327949df38ca4ea372b0001c60a31b2896f71cf0"
+}
diff --git a/rust/.sqlx/query-f31642455fadb1864fac3386a8da4822311da3cacd61269ef4f95705ea982002.json b/rust/.sqlx/query-f31642455fadb1864fac3386a8da4822311da3cacd61269ef4f95705ea982002.json
new file mode 100644
index 00000000..38864c42
--- /dev/null
+++ b/rust/.sqlx/query-f31642455fadb1864fac3386a8da4822311da3cacd61269ef4f95705ea982002.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n INSERT OR IGNORE INTO receipts(\n receipt_id, contact_id, message, contact_will_sends_receipt\n ) VALUES (?, ?, ?, 0)\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 3
+ },
+ "nullable": []
+ },
+ "hash": "f31642455fadb1864fac3386a8da4822311da3cacd61269ef4f95705ea982002"
+}
diff --git a/rust/.sqlx/query-f50e328e40de6f4f886a524118b6ef21dacefd2a248a2a8256cd100451d9889a.json b/rust/.sqlx/query-f50e328e40de6f4f886a524118b6ef21dacefd2a248a2a8256cd100451d9889a.json
deleted file mode 100644
index 0bc8b05e..00000000
--- a/rust/.sqlx/query-f50e328e40de6f4f886a524118b6ef21dacefd2a248a2a8256cd100451d9889a.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n INSERT INTO group_members(group_id, contact_id, member_state)\n VALUES (?, ?, 'member')\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "f50e328e40de6f4f886a524118b6ef21dacefd2a248a2a8256cd100451d9889a"
-}
diff --git a/rust/.sqlx/query-f82da34ecdaf448b9825b8e1bad094c75554c2b46d0f96a1714d31b402c9f49d.json b/rust/.sqlx/query-f82da34ecdaf448b9825b8e1bad094c75554c2b46d0f96a1714d31b402c9f49d.json
new file mode 100644
index 00000000..bb53e8b9
--- /dev/null
+++ b/rust/.sqlx/query-f82da34ecdaf448b9825b8e1bad094c75554c2b46d0f96a1714d31b402c9f49d.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT media_stored FROM messages WHERE message_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "media_stored",
+ "ordinal": 0,
+ "type_info": "Integer",
+ "origin": {
+ "Table": {
+ "table": "messages",
+ "name": "media_stored"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ false
+ ]
+ },
+ "hash": "f82da34ecdaf448b9825b8e1bad094c75554c2b46d0f96a1714d31b402c9f49d"
+}
diff --git a/rust/.sqlx/query-f93bfda82600aef970e0b7c17459ae774272af5db7898ea3594c9c921a932a66.json b/rust/.sqlx/query-f93bfda82600aef970e0b7c17459ae774272af5db7898ea3594c9c921a932a66.json
new file mode 100644
index 00000000..c4ae2b8a
--- /dev/null
+++ b/rust/.sqlx/query-f93bfda82600aef970e0b7c17459ae774272af5db7898ea3594c9c921a932a66.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT recovery_contacts_secret_share FROM contacts WHERE user_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "recovery_contacts_secret_share",
+ "ordinal": 0,
+ "type_info": "Blob",
+ "origin": {
+ "Table": {
+ "table": "contacts",
+ "name": "recovery_contacts_secret_share"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "f93bfda82600aef970e0b7c17459ae774272af5db7898ea3594c9c921a932a66"
+}
diff --git a/rust/.sqlx/query-f95f23d23817d46082ebc54610239cfe47265d64f80c833ee8d6495ddb6b90b4.json b/rust/.sqlx/query-f95f23d23817d46082ebc54610239cfe47265d64f80c833ee8d6495ddb6b90b4.json
new file mode 100644
index 00000000..66e4fe7c
--- /dev/null
+++ b/rust/.sqlx/query-f95f23d23817d46082ebc54610239cfe47265d64f80c833ee8d6495ddb6b90b4.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM groups WHERE group_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "f95f23d23817d46082ebc54610239cfe47265d64f80c833ee8d6495ddb6b90b4"
+}
diff --git a/rust/.sqlx/query-fa94eafde21afc4f8efdd64e95b7c2387451f1107ba53a4d1e3854a2c05737b7.json b/rust/.sqlx/query-fa94eafde21afc4f8efdd64e95b7c2387451f1107ba53a4d1e3854a2c05737b7.json
new file mode 100644
index 00000000..bd5421f5
--- /dev/null
+++ b/rust/.sqlx/query-fa94eafde21afc4f8efdd64e95b7c2387451f1107ba53a4d1e3854a2c05737b7.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "DELETE FROM receipts WHERE contact_id = ? AND message_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "fa94eafde21afc4f8efdd64e95b7c2387451f1107ba53a4d1e3854a2c05737b7"
+}
diff --git a/rust/.sqlx/query-fb9d21dc469738356e38205fc0addbbf82b85d6e4ec3090f7718dc692d7dbdf5.json b/rust/.sqlx/query-fb9d21dc469738356e38205fc0addbbf82b85d6e4ec3090f7718dc692d7dbdf5.json
new file mode 100644
index 00000000..39f1afad
--- /dev/null
+++ b/rust/.sqlx/query-fb9d21dc469738356e38205fc0addbbf82b85d6e4ec3090f7718dc692d7dbdf5.json
@@ -0,0 +1,26 @@
+{
+ "db_name": "SQLite",
+ "query": "SELECT member_state FROM group_members WHERE group_id = ? AND contact_id = ?",
+ "describe": {
+ "columns": [
+ {
+ "name": "member_state",
+ "ordinal": 0,
+ "type_info": "Text",
+ "origin": {
+ "Table": {
+ "table": "group_members",
+ "name": "member_state"
+ }
+ }
+ }
+ ],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": [
+ true
+ ]
+ },
+ "hash": "fb9d21dc469738356e38205fc0addbbf82b85d6e4ec3090f7718dc692d7dbdf5"
+}
diff --git a/rust/.sqlx/query-fc76c1c5acdd20c8645a77388c885ad47d08683c8ef4cb1e52ee106eb898a00d.json b/rust/.sqlx/query-fc76c1c5acdd20c8645a77388c885ad47d08683c8ef4cb1e52ee106eb898a00d.json
new file mode 100644
index 00000000..ad0b70ce
--- /dev/null
+++ b/rust/.sqlx/query-fc76c1c5acdd20c8645a77388c885ad47d08683c8ef4cb1e52ee106eb898a00d.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "INSERT OR IGNORE INTO group_members(group_id, contact_id) VALUES (?, ?)",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 2
+ },
+ "nullable": []
+ },
+ "hash": "fc76c1c5acdd20c8645a77388c885ad47d08683c8ef4cb1e52ee106eb898a00d"
+}
diff --git a/rust/.sqlx/query-fcba7a9b6c5c62a7086365f9120fec35a2a20a3472de9efe6f9fc55f6f2f19dd.json b/rust/.sqlx/query-fcba7a9b6c5c62a7086365f9120fec35a2a20a3472de9efe6f9fc55f6f2f19dd.json
new file mode 100644
index 00000000..b3e7c44d
--- /dev/null
+++ b/rust/.sqlx/query-fcba7a9b6c5c62a7086365f9120fec35a2a20a3472de9efe6f9fc55f6f2f19dd.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "\n UPDATE received_receipts\n SET created_at = CAST(strftime('%s', 'now') AS INTEGER)\n WHERE receipt_id = ?\n AND created_at <= CAST(strftime('%s', 'now') AS INTEGER) - 864000\n ",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 1
+ },
+ "nullable": []
+ },
+ "hash": "fcba7a9b6c5c62a7086365f9120fec35a2a20a3472de9efe6f9fc55f6f2f19dd"
+}
diff --git a/rust/.sqlx/query-febc50ab89aa3a9bbb2e9333d8547cb700bc3039c178e133def8a2dc2e8aabb6.json b/rust/.sqlx/query-febc50ab89aa3a9bbb2e9333d8547cb700bc3039c178e133def8a2dc2e8aabb6.json
deleted file mode 100644
index a6abc4a9..00000000
--- a/rust/.sqlx/query-febc50ab89aa3a9bbb2e9333d8547cb700bc3039c178e133def8a2dc2e8aabb6.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "db_name": "SQLite",
- "query": "\n UPDATE groups\n SET last_message_exchange = MAX(last_message_exchange, ?)\n WHERE group_id = ?\n ",
- "describe": {
- "columns": [],
- "parameters": {
- "Right": 2
- },
- "nullable": []
- },
- "hash": "febc50ab89aa3a9bbb2e9333d8547cb700bc3039c178e133def8a2dc2e8aabb6"
-}
diff --git a/rust/.sqlx/query-fedeeeaee2ad31906726430055edac23359bb61ff80d53632c95b40837d504f3.json b/rust/.sqlx/query-fedeeeaee2ad31906726430055edac23359bb61ff80d53632c95b40837d504f3.json
new file mode 100644
index 00000000..e4a07001
--- /dev/null
+++ b/rust/.sqlx/query-fedeeeaee2ad31906726430055edac23359bb61ff80d53632c95b40837d504f3.json
@@ -0,0 +1,12 @@
+{
+ "db_name": "SQLite",
+ "query": "UPDATE receipts SET message_id = ?, will_be_retried_by_media_upload = ?,\n retry_count = ?, last_retry = ? WHERE receipt_id = ?",
+ "describe": {
+ "columns": [],
+ "parameters": {
+ "Right": 5
+ },
+ "nullable": []
+ },
+ "hash": "fedeeeaee2ad31906726430055edac23359bb61ff80d53632c95b40837d504f3"
+}
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index d685e310..2a0cd447 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -176,6 +176,18 @@ dependencies = [
"derive_arbitrary",
]
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
+
[[package]]
name = "assert_matches"
version = "1.5.0"
@@ -256,7 +268,7 @@ version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
"cexpr",
"clang-sys",
"itertools 0.13.0",
@@ -270,6 +282,12 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
[[package]]
name = "bitflags"
version = "2.13.1"
@@ -574,6 +592,15 @@ dependencies = [
"rand 0.10.2",
]
+[[package]]
+name = "core_maths"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
+dependencies = [
+ "libm",
+]
+
[[package]]
name = "cpubits"
version = "0.1.1"
@@ -840,6 +867,12 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "data-url"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
+
[[package]]
name = "delegate-attr"
version = "0.3.0"
@@ -1016,6 +1049,15 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "euclid"
+version = "0.22.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "event-listener"
version = "5.4.2"
@@ -1043,6 +1085,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
[[package]]
name = "fiat-crypto"
version = "0.3.0"
@@ -1071,6 +1122,12 @@ dependencies = [
"miniz_oxide",
]
+[[package]]
+name = "float-cmp"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
+
[[package]]
name = "flume"
version = "0.12.0"
@@ -1137,6 +1194,18 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+[[package]]
+name = "fontdb"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905"
+dependencies = [
+ "log",
+ "slotmap",
+ "tinyvec",
+ "ttf-parser",
+]
+
[[package]]
name = "foreign-types"
version = "0.3.2"
@@ -1768,6 +1837,12 @@ dependencies = [
"icu_properties",
]
+[[package]]
+name = "imagesize"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
+
[[package]]
name = "indenter"
version = "0.3.4"
@@ -1917,6 +1992,17 @@ dependencies = [
"log",
]
+[[package]]
+name = "kurbo"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62"
+dependencies = [
+ "arrayvec",
+ "euclid",
+ "smallvec",
+]
+
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -2046,6 +2132,12 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
[[package]]
name = "libsignal-core"
version = "0.1.0"
@@ -2082,7 +2174,7 @@ dependencies = [
"aes-gcm-siv",
"assert_matches",
"async-trait",
- "bitflags",
+ "bitflags 2.13.1",
"const-str",
"ctr 0.10.1",
"data-encoding-macro",
@@ -2360,7 +2452,7 @@ version = "0.10.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
"cfg-if",
"foreign-types",
"libc",
@@ -2486,6 +2578,12 @@ dependencies = [
"indexmap",
]
+[[package]]
+name = "pico-args"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
+
[[package]]
name = "pin-project-lite"
version = "0.2.17"
@@ -2498,6 +2596,19 @@ version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+[[package]]
+name = "png"
+version = "0.17.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+dependencies = [
+ "bitflags 1.3.2",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
[[package]]
name = "poly1305"
version = "0.8.0"
@@ -2885,7 +2996,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
]
[[package]]
@@ -2955,6 +3066,29 @@ dependencies = [
"webpki-roots",
]
+[[package]]
+name = "resvg"
+version = "0.45.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43"
+dependencies = [
+ "log",
+ "pico-args",
+ "rgb",
+ "svgtypes",
+ "tiny-skia",
+ "usvg",
+]
+
+[[package]]
+name = "rgb"
+version = "0.8.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4"
+dependencies = [
+ "bytemuck",
+]
+
[[package]]
name = "ring"
version = "0.17.14"
@@ -2969,6 +3103,12 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "roxmltree"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
+
[[package]]
name = "rust_lib_twonly"
version = "0.1.0"
@@ -2989,6 +3129,7 @@ dependencies = [
"hex",
"hkdf 0.12.4",
"hmac 0.13.0",
+ "jni",
"keyring-core",
"libsignal-protocol",
"libsqlite3-sys",
@@ -3000,6 +3141,7 @@ dependencies = [
"rand 0.8.7",
"rand 0.9.5",
"reqwest",
+ "resvg",
"rustls",
"scrypt",
"serde",
@@ -3048,7 +3190,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
"errno",
"libc",
"linux-raw-sys",
@@ -3108,6 +3250,24 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
+[[package]]
+name = "rustybuzz"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702"
+dependencies = [
+ "bitflags 2.13.1",
+ "bytemuck",
+ "core_maths",
+ "log",
+ "smallvec",
+ "ttf-parser",
+ "unicode-bidi-mirroring",
+ "unicode-ccc",
+ "unicode-properties",
+ "unicode-script",
+]
+
[[package]]
name = "ryu"
version = "1.0.23"
@@ -3166,7 +3326,7 @@ version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -3351,12 +3511,36 @@ version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea"
+[[package]]
+name = "simplecss"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "siphasher"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649"
+
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+[[package]]
+name = "slotmap"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
+dependencies = [
+ "version_check",
+]
+
[[package]]
name = "smallvec"
version = "1.15.2"
@@ -3507,7 +3691,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90b8020fe17c5f2c245bfa2505d7ef59c5604839527c740266ad2214acebea27"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
"byteorder",
"bytes",
"chrono",
@@ -3536,7 +3720,7 @@ checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e"
dependencies = [
"atoi",
"base64",
- "bitflags",
+ "bitflags 2.13.1",
"byteorder",
"chrono",
"crc",
@@ -3618,6 +3802,15 @@ dependencies = [
"tungstenite",
]
+[[package]]
+name = "strict-num"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
+dependencies = [
+ "float-cmp",
+]
+
[[package]]
name = "stringprep"
version = "0.1.5"
@@ -3641,6 +3834,16 @@ version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+[[package]]
+name = "svgtypes"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
+dependencies = [
+ "kurbo",
+ "siphasher",
+]
+
[[package]]
name = "symlink"
version = "0.1.0"
@@ -3799,6 +4002,32 @@ dependencies = [
"time-core",
]
+[[package]]
+name = "tiny-skia"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "bytemuck",
+ "cfg-if",
+ "log",
+ "png",
+ "tiny-skia-path",
+]
+
+[[package]]
+name = "tiny-skia-path"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
+dependencies = [
+ "arrayref",
+ "bytemuck",
+ "strict-num",
+]
+
[[package]]
name = "tinystr"
version = "0.8.3"
@@ -3966,7 +4195,7 @@ version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
dependencies = [
- "bitflags",
+ "bitflags 2.13.1",
"bytes",
"futures-util",
"http",
@@ -4071,6 +4300,15 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+dependencies = [
+ "core_maths",
+]
+
[[package]]
name = "tungstenite"
version = "0.28.0"
@@ -4103,6 +4341,18 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
+[[package]]
+name = "unicode-bidi-mirroring"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe"
+
+[[package]]
+name = "unicode-ccc"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e"
+
[[package]]
name = "unicode-ident"
version = "1.0.24"
@@ -4124,6 +4374,18 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
+[[package]]
+name = "unicode-script"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee"
+
+[[package]]
+name = "unicode-vo"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
+
[[package]]
name = "universal-hash"
version = "0.5.1"
@@ -4162,6 +4424,33 @@ dependencies = [
"serde",
]
+[[package]]
+name = "usvg"
+version = "0.45.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef"
+dependencies = [
+ "base64",
+ "data-url",
+ "flate2",
+ "fontdb",
+ "imagesize",
+ "kurbo",
+ "log",
+ "pico-args",
+ "roxmltree",
+ "rustybuzz",
+ "simplecss",
+ "siphasher",
+ "strict-num",
+ "svgtypes",
+ "tiny-skia-path",
+ "unicode-bidi",
+ "unicode-script",
+ "unicode-vo",
+ "xmlwriter",
+]
+
[[package]]
name = "utf-8"
version = "0.7.6"
@@ -4575,6 +4864,12 @@ dependencies = [
"zeroize",
]
+[[package]]
+name = "xmlwriter"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
+
[[package]]
name = "yoke"
version = "0.8.3"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index ffa08812..83755eb1 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -52,6 +52,7 @@ rand08 = { version = "0.8.5", package = "rand" }
rand = "0.9.4"
uuid = { version = "1", features = ["v4"] }
flate2 = "1"
+resvg = { version = "0.45", default-features = false, features = ["text"] }
reqwest = { version = "0.12", default-features = false, features = [
"rustls-tls",
] }
@@ -74,6 +75,7 @@ apple-native-keyring-store = { version = "1", features = ["protected"] }
[target.'cfg(target_os = "android")'.dependencies]
# Android backend: Interfaces with the Android Keystore
android-native-keyring-store = "1"
+jni = "0.21.1"
[dev-dependencies]
anyhow = "1.0.104"
diff --git a/rust/src/api/messages/incoming.rs b/rust/src/api/messages/incoming.rs
index 30eed7e2..e00b155c 100644
--- a/rust/src/api/messages/incoming.rs
+++ b/rust/src/api/messages/incoming.rs
@@ -45,13 +45,13 @@ pub(crate) async fn handle_server_message(
Kind::RequestNewPqcPreKeys(_) => match handle_request_new_pqc_prekeys(ctx).await {
Ok(response) => response,
Err(error) => {
- tracing::error!("failed to generate requested PQC prekeys: {error}");
+ tracing::warn!("failed to generate requested PQC prekeys: {error}");
ok::Ok::None(true)
}
},
Kind::NewMessage(message) => {
if let Err(error) = handle_new_server_message(ctx, message).await {
- tracing::error!("failed to process client message: {error}");
+ tracing::warn!("failed to process client message: {error}");
}
ok::Ok::None(true)
}
@@ -59,29 +59,33 @@ pub(crate) async fn handle_server_message(
for message in messages.new_messages {
if let Err(error) = handle_new_server_message(ctx, message).await {
// One bad item must not block the rest of a server batch.
- tracing::error!("failed to process client message in batch: {error}");
+ tracing::warn!("failed to process client message in batch: {error}");
}
}
ok::Ok::None(true)
}
Kind::SealedSenderMessage(message) => {
if let Err(error) = handle_sealed_message(ctx, message.body).await {
- tracing::error!("failed to process sealed-sender message: {error}");
+ tracing::warn!("failed to process sealed-sender message: {error}");
}
ok::Ok::None(true)
}
Kind::SealedSenderMessages(messages) => {
for message in messages.messages {
if let Err(error) = handle_sealed_message(ctx, message.body).await {
- tracing::error!("failed to process sealed-sender message in batch: {error}");
+ tracing::warn!("failed to process sealed-sender message in batch: {error}");
}
}
ok::Ok::None(true)
}
+ Kind::MailboxDrained(_) => {
+ ctx.mark_mailbox_drained();
+ ok::Ok::None(true)
+ }
other => {
// Dart logged unknown unsolicited messages but still acknowledged
// their envelope, preventing an infinite server redelivery loop.
- tracing::error!("unsupported unsolicited server message: {other:?}");
+ tracing::warn!("unsupported unsolicited server message: {other:?}");
ok::Ok::None(true)
}
};
@@ -305,6 +309,7 @@ pub(crate) async fn handle_decoded_server_message(
}
t.commit().await?;
+ ctx.mark_incoming_committed();
database.notify_committed([
"received_receipts",
@@ -314,6 +319,7 @@ pub(crate) async fn handle_decoded_server_message(
"contacts",
"key_verifications",
"user_discovery_own_promotions",
+ "notification_outbox",
]);
let ctx = ctx.clone();
@@ -335,6 +341,24 @@ pub(crate) async fn handle_encrypted(
from_user_id: i64,
receipt_id: &str,
content: proto::EncryptedContent,
+) -> Result<()> {
+ let notification_content = content.clone();
+ handle_encrypted_inner(ctx, t, from_user_id, receipt_id, content).await?;
+ crate::services::notifications::record_incoming_event(
+ t,
+ from_user_id,
+ receipt_id,
+ ¬ification_content,
+ )
+ .await
+}
+
+async fn handle_encrypted_inner(
+ ctx: &Arc,
+ t: &mut Transaction<'_, Sqlite>,
+ from_user_id: i64,
+ receipt_id: &str,
+ content: proto::EncryptedContent,
) -> Result<()> {
Receipt::mark_all_for_retry(t, from_user_id).await?;
diff --git a/rust/src/api/messages/incoming/additional_data.rs b/rust/src/api/messages/incoming/additional_data.rs
index 2ef17c0a..ef7f3fe5 100644
--- a/rust/src/api/messages/incoming/additional_data.rs
+++ b/rust/src/api/messages/incoming/additional_data.rs
@@ -78,7 +78,7 @@ async fn verify_shared_contacts(
};
if stored_identity != contact.public_identity_key {
- tracing::error!("shared contact public identity key does not match");
+ tracing::warn!("shared contact public identity key does not match");
continue;
}
diff --git a/rust/src/api/messages/incoming/messages.rs b/rust/src/api/messages/incoming/messages.rs
index c42b86e4..219f2a01 100644
--- a/rust/src/api/messages/incoming/messages.rs
+++ b/rust/src/api/messages/incoming/messages.rs
@@ -32,6 +32,8 @@ pub(crate) async fn queue_encrypted_content(
) -> Result {
Contact::ensure_exists(t, target_user_id).await?;
+ let wake_receiver = crate::services::notifications::should_wake_receiver(&content);
+
let message = proto::Message {
r#type: proto::message::Type::CiphertextV2 as i32,
receipt_id: String::new(),
@@ -44,6 +46,7 @@ pub(crate) async fn queue_encrypted_content(
NewReceipt::new(&receipt_id, target_user_id, &message)
.contact_will_send_receipt(contact_will_send_receipt)
+ .wake_receiver(wake_receiver)
.insert(t)
.await?;
@@ -297,6 +300,17 @@ pub(crate) struct PreparedQueuedReceipt {
pub contact_will_sends_receipt: i64,
pub account_deleted: i64,
pub payload: Vec,
+ pub wake_receiver: bool,
+}
+
+struct PreparedQueuedReceiptRow {
+ contact_id: i64,
+ message: Vec,
+ message_id: Option,
+ contact_will_sends_receipt: i64,
+ wake_receiver: i64,
+ account_deleted: i64,
+ signal_version: String,
}
pub(crate) async fn prepare_queued_receipt_details(
@@ -304,10 +318,11 @@ pub(crate) async fn prepare_queued_receipt_details(
receipt_id: &str,
) -> Result