@@ -8,13 +8,15 @@ import androidx.work.workDataOf
88import dagger.assisted.Assisted
99import dagger.assisted.AssistedInject
1010import kotlinx.coroutines.CompletableDeferred
11+ import kotlinx.coroutines.delay
1112import kotlinx.coroutines.flow.first
1213import kotlinx.coroutines.withTimeout
1314import kotlinx.serialization.json.JsonObject
1415import kotlinx.serialization.json.JsonPrimitive
1516import kotlinx.serialization.json.contentOrNull
1617import kotlinx.serialization.json.jsonObject
1718import org.lightningdevkit.ldknode.Event
19+ import to.bitkit.App
1820import to.bitkit.R
1921import to.bitkit.data.CacheStore
2022import to.bitkit.data.SettingsStore
@@ -34,26 +36,23 @@ import to.bitkit.models.NotificationDetails
3436import to.bitkit.repositories.ActivityRepo
3537import to.bitkit.repositories.BlocktankRepo
3638import to.bitkit.repositories.LightningRepo
37- import to.bitkit.services.CoreService
3839import to.bitkit.ui.pushNotification
3940import to.bitkit.utils.Logger
4041import to.bitkit.utils.measured
4142import kotlin.time.Duration.Companion.minutes
43+ import kotlin.time.Duration.Companion.seconds
4244
4345@Suppress(" LongParameterList" )
4446@HiltWorker
4547class WakeNodeWorker @AssistedInject constructor(
4648 @Assisted private val appContext : Context ,
4749 @Assisted private val workerParams : WorkerParameters ,
48- private val coreService : CoreService ,
4950 private val lightningRepo : LightningRepo ,
5051 private val blocktankRepo : BlocktankRepo ,
5152 private val activityRepo : ActivityRepo ,
5253 private val settingsStore : SettingsStore ,
5354 private val cacheStore : CacheStore ,
5455) : CoroutineWorker(appContext, workerParams) {
55- private val self = this
56-
5756 private var bestAttemptContent: NotificationDetails ? = null
5857
5958 private var notificationType: BlocktankNotificationType ? = null
@@ -63,15 +62,19 @@ class WakeNodeWorker @AssistedInject constructor(
6362 private val deliverSignal = CompletableDeferred <Unit >()
6463
6564 override suspend fun doWork (): Result {
66- Logger .debug(" Node wakeup from notification…" )
65+ Logger .debug(" Node wakeup from notification…" , context = TAG )
6766
6867 notificationType = workerParams.inputData.getString(" type" )?.let { BlocktankNotificationType .valueOf(it) }
6968 notificationPayload = workerParams.inputData.getString(" payload" )?.let {
7069 runCatching { json.parseToJsonElement(it).jsonObject }.getOrNull()
7170 }
7271
73- Logger .debug(" ${this ::class .simpleName} notification type: $notificationType " )
74- Logger .debug(" ${this ::class .simpleName} notification payload: $notificationPayload " )
72+ Logger .debug(" notification type: $notificationType " , context = TAG )
73+ Logger .debug(" notification payload: $notificationPayload " , context = TAG )
74+
75+ if (notificationType == null ) {
76+ Logger .warn(" Notification type is null, proceeding with node wake" , context = TAG )
77+ }
7578
7679 try {
7780 measured(TAG ) {
@@ -80,25 +83,22 @@ class WakeNodeWorker @AssistedInject constructor(
8083 timeout = timeout,
8184 eventHandler = { event -> handleLdkEvent(event) }
8285 )
83- lightningRepo.connectToTrustedPeers()
8486
8587 // Once node is started, handle the manual channel opening if needed
86- if (self. notificationType == orderPaymentConfirmed) {
88+ if (notificationType == orderPaymentConfirmed) {
8789 val orderId = (notificationPayload?.get(" orderId" ) as ? JsonPrimitive )?.contentOrNull
8890
8991 if (orderId == null ) {
90- Logger .error(" Missing orderId" )
92+ Logger .error(" Missing orderId" , context = TAG )
9193 } else {
92- try {
93- Logger .info(" Open channel request for order $orderId " )
94- coreService.blocktank.open(orderId = orderId)
95- } catch (e: Exception ) {
96- Logger .error(" failed to open channel" , e)
97- self.bestAttemptContent = NotificationDetails (
94+ Logger .info(" Open channel request for order $orderId " , context = TAG )
95+ blocktankRepo.openChannel(orderId).onFailure { e ->
96+ Logger .error(" Failed to open channel" , e, context = TAG )
97+ bestAttemptContent = NotificationDetails (
9898 title = appContext.getString(R .string.notification_channel_open_failed_title),
9999 body = e.message ? : appContext.getString(R .string.notification_unknown_error),
100100 )
101- self. deliver()
101+ deliver()
102102 }
103103 }
104104 }
@@ -108,12 +108,12 @@ class WakeNodeWorker @AssistedInject constructor(
108108 } catch (e: Exception ) {
109109 val reason = e.message ? : appContext.getString(R .string.notification_unknown_error)
110110
111- self. bestAttemptContent = NotificationDetails (
111+ bestAttemptContent = NotificationDetails (
112112 title = appContext.getString(R .string.notification_lightning_error_title),
113113 body = reason,
114114 )
115- Logger .error(" Lightning error" , e)
116- self. deliver()
115+ Logger .error(" Lightning error" , e, context = TAG )
116+ deliver()
117117
118118 return Result .failure(workDataOf(" Reason" to reason))
119119 }
@@ -130,7 +130,7 @@ class WakeNodeWorker @AssistedInject constructor(
130130 is Event .PaymentReceived -> onPaymentReceived(event, showDetails, hiddenBody)
131131
132132 is Event .ChannelPending -> {
133- self. bestAttemptContent = NotificationDetails (
133+ bestAttemptContent = NotificationDetails (
134134 title = appContext.getString(R .string.notification_channel_opened_title),
135135 body = appContext.getString(R .string.notification_channel_pending_body),
136136 )
@@ -141,13 +141,13 @@ class WakeNodeWorker @AssistedInject constructor(
141141 is Event .ChannelClosed -> onChannelClosed(event)
142142
143143 is Event .PaymentFailed -> {
144- self. bestAttemptContent = NotificationDetails (
144+ bestAttemptContent = NotificationDetails (
145145 title = appContext.getString(R .string.notification_payment_failed_title),
146146 body = " ⚡ ${event.reason} " ,
147147 )
148148
149- if (self. notificationType == wakeToTimeout) {
150- self. deliver()
149+ if (notificationType == wakeToTimeout) {
150+ deliver()
151151 }
152152 }
153153
@@ -156,7 +156,7 @@ class WakeNodeWorker @AssistedInject constructor(
156156 }
157157
158158 private suspend fun onChannelClosed (event : Event .ChannelClosed ) {
159- self. bestAttemptContent = when (self. notificationType) {
159+ bestAttemptContent = when (notificationType) {
160160 mutualClose -> NotificationDetails (
161161 title = appContext.getString(R .string.notification_channel_closed_title),
162162 body = appContext.getString(R .string.notification_channel_closed_mutual_body),
@@ -173,7 +173,7 @@ class WakeNodeWorker @AssistedInject constructor(
173173 )
174174 }
175175
176- self. deliver()
176+ deliver()
177177 }
178178
179179 private suspend fun onPaymentReceived (
@@ -196,8 +196,8 @@ class WakeNodeWorker @AssistedInject constructor(
196196 title = appContext.getString(R .string.notification_received_title),
197197 body = content,
198198 )
199- if (self. notificationType == incomingHtlc) {
200- self. deliver()
199+ if (notificationType == incomingHtlc) {
200+ deliver()
201201 }
202202 }
203203
@@ -207,16 +207,16 @@ class WakeNodeWorker @AssistedInject constructor(
207207 hiddenBody : String ,
208208 ) {
209209 val viaNewChannel = appContext.getString(R .string.notification_via_new_channel_body)
210- if (self. notificationType == cjitPaymentArrived) {
211- self. bestAttemptContent = NotificationDetails (
210+ if (notificationType == cjitPaymentArrived) {
211+ bestAttemptContent = NotificationDetails (
212212 title = appContext.getString(R .string.notification_received_title),
213213 body = viaNewChannel,
214214 )
215215
216216 lightningRepo.getChannels()?.find { it.channelId == event.channelId }?.let { channel ->
217217 val sats = channel.amountOnClose
218218 val content = if (showDetails) " $BITCOIN_SYMBOL $sats " else hiddenBody
219- self. bestAttemptContent = NotificationDetails (
219+ bestAttemptContent = NotificationDetails (
220220 title = content,
221221 body = viaNewChannel,
222222 )
@@ -233,21 +233,32 @@ class WakeNodeWorker @AssistedInject constructor(
233233 activityRepo.insertActivityFromCjit(cjitEntry = cjitEntry, channel = channel)
234234 }
235235 }
236- } else if (self. notificationType == orderPaymentConfirmed) {
237- self. bestAttemptContent = NotificationDetails (
236+ } else if (notificationType == orderPaymentConfirmed) {
237+ bestAttemptContent = NotificationDetails (
238238 title = appContext.getString(R .string.notification_channel_opened_title),
239239 body = appContext.getString(R .string.notification_channel_ready_body),
240240 )
241241 }
242- self. deliver()
242+ deliver()
243243 }
244244
245245 private suspend fun deliver () {
246- lightningRepo.stop()
247-
246+ // Send notification first
248247 bestAttemptContent?.run {
249248 appContext.pushNotification(title, body)
250- Logger .info(" Delivered notification" )
249+ Logger .info(" Delivered notification" , context = TAG )
250+ }
251+
252+ // Delay briefly to allow app to come to foreground if user clicked notification
253+ delay(1 .seconds)
254+
255+ // Only stop node if app is not in foreground
256+ // LightningNodeService will keep node running in background when notifications are enabled
257+ if (App .currentActivity?.value == null ) {
258+ Logger .debug(" App in background, stopping node after notification delivery" , context = TAG )
259+ lightningRepo.stop()
260+ } else {
261+ Logger .debug(" App in foreground, keeping node running" , context = TAG )
251262 }
252263
253264 deliverSignal.complete(Unit )
0 commit comments