From b10ea04cb38697ddd6a9cfbe6bc74bc10ada941f Mon Sep 17 00:00:00 2001 From: Jon Staab Date: Fri, 10 Apr 2026 11:55:26 -0700 Subject: [PATCH] Fix Android push fallback: show all notifications, retry on failure --- .fdignore | 2 +- .../AndroidPushFallbackPlugin.kt | 2 ++ .../AndroidPushFallbackWorker.kt | 19 +++++++++---------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/.fdignore b/.fdignore index 8d2a3cfa..fbd68f6c 100644 --- a/.fdignore +++ b/.fdignore @@ -13,4 +13,4 @@ ios/App/Pods/ android/capacitor-cordova-android-plugins android/app/src/androidTest android/app/src/test - +node_modules diff --git a/android/app/src/main/java/social/flotilla/notifications/AndroidPushFallbackPlugin.kt b/android/app/src/main/java/social/flotilla/notifications/AndroidPushFallbackPlugin.kt index dff32c52..d394c984 100644 --- a/android/app/src/main/java/social/flotilla/notifications/AndroidPushFallbackPlugin.kt +++ b/android/app/src/main/java/social/flotilla/notifications/AndroidPushFallbackPlugin.kt @@ -7,6 +7,7 @@ import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType import androidx.work.OneTimeWorkRequest +import androidx.work.OutOfQuotaPolicy import androidx.work.PeriodicWorkRequest import androidx.work.WorkManager import com.getcapacitor.JSObject @@ -76,6 +77,7 @@ class AndroidPushFallbackPlugin : Plugin() { val immediate = OneTimeWorkRequest.Builder(AndroidPushFallbackWorker::class.java) .setConstraints(constraints) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .build() workManager.enqueueUniquePeriodicWork( diff --git a/android/app/src/main/java/social/flotilla/notifications/AndroidPushFallbackWorker.kt b/android/app/src/main/java/social/flotilla/notifications/AndroidPushFallbackWorker.kt index 0466ccc3..1aee3eff 100644 --- a/android/app/src/main/java/social/flotilla/notifications/AndroidPushFallbackWorker.kt +++ b/android/app/src/main/java/social/flotilla/notifications/AndroidPushFallbackWorker.kt @@ -43,7 +43,7 @@ class AndroidPushFallbackWorker(context: Context, params: WorkerParameters) : Wo private const val TAG = "PushFallback" private const val CHANNEL_ID = "flotilla_fallback" private const val CURSOR_PREFIX = "androidPushFallback.cursor." - private const val SOCKET_TIMEOUT_SECONDS = 20L + private const val SOCKET_TIMEOUT_SECONDS = 30L private const val REJECTED = "__REJECTED__" private const val KIND_RELAY_AUTH = 22242 private const val KIND_NIP46_RPC = 24133 @@ -72,6 +72,8 @@ class AndroidPushFallbackWorker(context: Context, params: WorkerParameters) : Wo } override fun doWork(): Result { + Log.i(TAG, "doWork() started") + if (isAppInForeground()) { return Result.success() } @@ -88,7 +90,7 @@ class AndroidPushFallbackWorker(context: Context, params: WorkerParameters) : Wo val activeSince = state.optLong("activeSince", 0L) val seen = mutableSetOf() - var latestPair: Pair? = null + val newEvents = mutableListOf>() for (sub in subscriptions) { val cursorKey = CURSOR_PREFIX + sub.relay + ":" + sub.key @@ -102,23 +104,19 @@ class AndroidPushFallbackWorker(context: Context, params: WorkerParameters) : Wo for (event in result.events) { val id = event.optString("id", "") if (id.isNotEmpty() && seen.add(id)) { - val createdAt = event.optLong("created_at", 0L) - if (latestPair == null || createdAt > latestPair!!.second.optLong("created_at", 0L)) { - latestPair = Pair(sub.relay, event) - } + newEvents.add(Pair(sub.relay, event)) } } } - if (latestPair != null) { - val (relay, event) = latestPair!! + for ((relay, event) in newEvents) { postNotification(relay, event) } return Result.success() } catch (e: Exception) { Log.e(TAG, "Worker failed", e) - return Result.success() + return Result.retry() } finally { pool.closeAll() client.dispatcher.executorService.shutdown() @@ -214,7 +212,8 @@ class AndroidPushFallbackWorker(context: Context, params: WorkerParameters) : Wo .setContentIntent(pendingIntent) .build() - NotificationManagerCompat.from(context).notify(1, notification) + val notificationId = id.hashCode().let { if (it == 0) 1 else it } + NotificationManagerCompat.from(context).notify(notificationId, notification) } private fun matchesFilter(filter: JSONObject, event: JSONObject): Boolean {