Skip to content

Commit 06d271d

Browse files
committed
feat: move cached receive details to cache store
1 parent 8d5521a commit 06d271d

File tree

17 files changed

+277
-277
lines changed

17 files changed

+277
-277
lines changed

app/src/main/java/to/bitkit/androidServices/LightningNodeService.kt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ import kotlinx.coroutines.launch
1515
import org.lightningdevkit.ldknode.Event
1616
import to.bitkit.App
1717
import to.bitkit.R
18+
import to.bitkit.data.CacheStore
1819
import to.bitkit.domain.commands.NotifyPaymentReceived
1920
import to.bitkit.domain.commands.NotifyPaymentReceivedHandler
2021
import to.bitkit.models.NewTransactionSheetDetails
21-
import to.bitkit.models.NotificationState
22+
import to.bitkit.models.NotificationDetails
2223
import to.bitkit.repositories.LightningRepo
2324
import to.bitkit.repositories.WalletRepo
2425
import to.bitkit.services.LdkNodeEventBus
@@ -44,6 +45,9 @@ class LightningNodeService : Service() {
4445
@Inject
4546
lateinit var notifyPaymentReceivedHandler: NotifyPaymentReceivedHandler
4647

48+
@Inject
49+
lateinit var cacheStore: CacheStore
50+
4751
override fun onCreate() {
4852
super.onCreate()
4953
startForeground(NOTIFICATION_ID, createNotification())
@@ -79,20 +83,18 @@ class LightningNodeService : Service() {
7983
val command = NotifyPaymentReceived.Command.from(event, includeNotification = true) ?: return
8084

8185
notifyPaymentReceivedHandler(command).onSuccess { result ->
82-
if (result !is NotifyPaymentReceived.Result.ShowNotification) return@onSuccess
83-
if (App.currentActivity?.value != null) return@onSuccess
84-
85-
showPaymentNotification(result.details, result.notification)
86+
if (result !is NotifyPaymentReceived.Result.ShowNotification) return
87+
showPaymentNotification(result.sheet, result.notification)
8688
}
8789
}
8890

8991
private fun showPaymentNotification(
90-
details: NewTransactionSheetDetails,
91-
notification: NotificationState,
92+
sheet: NewTransactionSheetDetails,
93+
notification: NotificationDetails,
9294
) {
9395
if (App.currentActivity?.value != null) return
94-
NewTransactionSheetDetails.save(this, details)
95-
pushNotification(notification.title, notification.body, context = this)
96+
serviceScope.launch { cacheStore.setBackgroundReceive(sheet) }
97+
pushNotification(notification.title, notification.body)
9698
}
9799

98100
private fun createNotification(

app/src/main/java/to/bitkit/data/CacheStore.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import to.bitkit.models.BackupCategory
1414
import to.bitkit.models.BackupItemStatus
1515
import to.bitkit.models.BalanceState
1616
import to.bitkit.models.FxRate
17+
import to.bitkit.models.NewTransactionSheetDetails
1718
import to.bitkit.utils.Logger
1819
import javax.inject.Inject
1920
import javax.inject.Singleton
@@ -101,6 +102,14 @@ class CacheStore @Inject constructor(
101102
store.updateData { it.copy(lastLightningPaymentId = paymentId) }
102103
}
103104

105+
suspend fun setBackgroundReceive(details: NewTransactionSheetDetails) = store.updateData {
106+
it.copy(backgroundReceive = details)
107+
}
108+
109+
suspend fun clearBackgroundReceive() {
110+
store.updateData { it.copy(backgroundReceive = null) }
111+
}
112+
104113
suspend fun reset() {
105114
store.updateData { AppCacheData() }
106115
Logger.info("Deleted all app cached data.")
@@ -123,4 +132,5 @@ data class AppCacheData(
123132
val deletedActivities: List<String> = listOf(),
124133
val lastLightningPaymentId: String? = null,
125134
val pendingBoostActivities: List<PendingBoostActivity> = listOf(),
135+
val backgroundReceive: NewTransactionSheetDetails? = null,
126136
)

app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceived.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package to.bitkit.domain.commands
22

33
import org.lightningdevkit.ldknode.Event
44
import to.bitkit.models.NewTransactionSheetDetails
5-
import to.bitkit.models.NotificationState
5+
import to.bitkit.models.NotificationDetails
66

77
sealed interface NotifyPaymentReceived {
88

@@ -50,12 +50,12 @@ sealed interface NotifyPaymentReceived {
5050

5151
sealed interface Result : NotifyPaymentReceived {
5252
data class ShowSheet(
53-
val details: NewTransactionSheetDetails,
53+
val sheet: NewTransactionSheetDetails,
5454
) : Result
5555

5656
data class ShowNotification(
57-
val details: NewTransactionSheetDetails,
58-
val notification: NotificationState,
57+
val sheet: NewTransactionSheetDetails,
58+
val notification: NotificationDetails,
5959
) : Result
6060

6161
data object Skip : Result

app/src/main/java/to/bitkit/domain/commands/NotifyPaymentReceivedHandler.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import to.bitkit.models.BITCOIN_SYMBOL
1414
import to.bitkit.models.NewTransactionSheetDetails
1515
import to.bitkit.models.NewTransactionSheetDirection
1616
import to.bitkit.models.NewTransactionSheetType
17-
import to.bitkit.models.NotificationState
17+
import to.bitkit.models.NotificationDetails
1818
import to.bitkit.models.PrimaryDisplay
1919
import to.bitkit.models.formatToModernDisplay
2020
import to.bitkit.repositories.ActivityRepo
@@ -67,15 +67,15 @@ class NotifyPaymentReceivedHandler @Inject constructor(
6767
}
6868
}
6969

70-
private suspend fun buildNotificationContent(sats: Long): NotificationState {
70+
private suspend fun buildNotificationContent(sats: Long): NotificationDetails {
7171
val settings = settingsStore.data.first()
7272
val title = context.getString(R.string.notification_received_title)
7373
val body = if (settings.showNotificationDetails) {
7474
formatNotificationAmount(sats, settings)
7575
} else {
7676
context.getString(R.string.notification_received_body_hidden)
7777
}
78-
return NotificationState(title, body)
78+
return NotificationDetails(title, body)
7979
}
8080

8181
private fun formatNotificationAmount(sats: Long, settings: SettingsData): String {
@@ -95,6 +95,11 @@ class NotifyPaymentReceivedHandler @Inject constructor(
9595

9696
companion object {
9797
const val TAG = "NotifyPaymentReceivedHandler"
98+
99+
/**
100+
* Delay before calling `shouldShowPaymentReceived` for onchain transactions to allow ActivityRepo
101+
* to sync payments before we check for RBF replacement or channel closure.
102+
*/
98103
private const val DELAY_FOR_ACTIVITY_SYNC_MS = 500L
99104
}
100105
}

app/src/main/java/to/bitkit/fcm/FcmService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class FcmService : FirebaseMessagingService() {
136136
}
137137

138138
private fun sendNotification(title: String?, body: String?, extras: Bundle? = null) {
139-
pushNotification(title, body, extras, context = applicationContext)
139+
applicationContext.pushNotification(title, body, extras)
140140
}
141141

142142
private inline fun <reified T> Map<String, String>.tryAs(block: (T) -> Unit): Boolean {

app/src/main/java/to/bitkit/fcm/WakeNodeWorker.kt

Lines changed: 62 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import kotlinx.serialization.json.JsonPrimitive
1515
import kotlinx.serialization.json.contentOrNull
1616
import kotlinx.serialization.json.jsonObject
1717
import org.lightningdevkit.ldknode.Event
18+
import to.bitkit.data.CacheStore
1819
import to.bitkit.data.SettingsStore
1920
import to.bitkit.di.json
2021
import to.bitkit.ext.amountOnClose
@@ -28,10 +29,12 @@ import to.bitkit.models.BlocktankNotificationType.wakeToTimeout
2829
import to.bitkit.models.NewTransactionSheetDetails
2930
import to.bitkit.models.NewTransactionSheetDirection
3031
import to.bitkit.models.NewTransactionSheetType
32+
import to.bitkit.models.NotificationDetails
3133
import to.bitkit.repositories.ActivityRepo
3234
import to.bitkit.repositories.BlocktankRepo
3335
import to.bitkit.repositories.LightningRepo
3436
import to.bitkit.services.CoreService
37+
import to.bitkit.R
3538
import to.bitkit.ui.pushNotification
3639
import to.bitkit.utils.Logger
3740
import to.bitkit.utils.withPerformanceLogging
@@ -47,13 +50,11 @@ class WakeNodeWorker @AssistedInject constructor(
4750
private val blocktankRepo: BlocktankRepo,
4851
private val activityRepo: ActivityRepo,
4952
private val settingsStore: SettingsStore,
53+
private val cacheStore: CacheStore,
5054
) : CoroutineWorker(appContext, workerParams) {
5155
private val self = this
5256

53-
// TODO extract as global model and turn into data class.
54-
class VisibleNotification(var title: String = "", var body: String = "")
55-
56-
private var bestAttemptContent: VisibleNotification? = VisibleNotification()
57+
private var bestAttemptContent: NotificationDetails? = null
5758

5859
private var notificationType: BlocktankNotificationType? = null
5960
private var notificationPayload: JsonObject? = null
@@ -93,8 +94,10 @@ class WakeNodeWorker @AssistedInject constructor(
9394
coreService.blocktank.open(orderId = orderId)
9495
} catch (e: Exception) {
9596
Logger.error("failed to open channel", e)
96-
self.bestAttemptContent?.title = "Channel open failed"
97-
self.bestAttemptContent?.body = e.message ?: "Unknown error"
97+
self.bestAttemptContent = NotificationDetails(
98+
title = appContext.getString(R.string.notification_channel_open_failed_title),
99+
body = e.message ?: appContext.getString(R.string.notification_unknown_error),
100+
)
98101
self.deliver()
99102
}
100103
}
@@ -103,10 +106,12 @@ class WakeNodeWorker @AssistedInject constructor(
103106
withTimeout(timeout) { deliverSignal.await() } // Stops node on timeout & avoids notification replay by OS
104107
return Result.success()
105108
} catch (e: Exception) {
106-
val reason = e.message ?: "Unknown error"
109+
val reason = e.message ?: appContext.getString(R.string.notification_unknown_error)
107110

108-
self.bestAttemptContent?.title = "Lightning error"
109-
self.bestAttemptContent?.body = reason
111+
self.bestAttemptContent = NotificationDetails(
112+
title = appContext.getString(R.string.notification_lightning_error_title),
113+
body = reason,
114+
)
110115
Logger.error("Lightning error", e)
111116
self.deliver()
112117

@@ -120,22 +125,26 @@ class WakeNodeWorker @AssistedInject constructor(
120125
*/
121126
private suspend fun handleLdkEvent(event: Event) {
122127
val showDetails = settingsStore.data.first().showNotificationDetails
123-
val openBitkitMessage = "Open Bitkit to see details"
128+
val hiddenBody = appContext.getString(R.string.notification_received_body_hidden)
124129
when (event) {
125-
is Event.PaymentReceived -> onPaymentReceived(event, showDetails, openBitkitMessage)
130+
is Event.PaymentReceived -> onPaymentReceived(event, showDetails, hiddenBody)
126131

127132
is Event.ChannelPending -> {
128-
self.bestAttemptContent?.title = "Channel Opened"
129-
self.bestAttemptContent?.body = "Pending"
133+
self.bestAttemptContent = NotificationDetails(
134+
title = appContext.getString(R.string.notification_channel_opened_title),
135+
body = appContext.getString(R.string.notification_channel_pending_body),
136+
)
130137
// Don't deliver, give a chance for channelReady event to update the content if it's a turbo channel
131138
}
132139

133-
is Event.ChannelReady -> onChannelReady(event, showDetails, openBitkitMessage)
140+
is Event.ChannelReady -> onChannelReady(event, showDetails, hiddenBody)
134141
is Event.ChannelClosed -> onChannelClosed(event)
135142

136143
is Event.PaymentFailed -> {
137-
self.bestAttemptContent?.title = "Payment failed"
138-
self.bestAttemptContent?.body = "${event.reason}"
144+
self.bestAttemptContent = NotificationDetails(
145+
title = appContext.getString(R.string.notification_payment_failed_title),
146+
body = "${event.reason}",
147+
)
139148

140149
if (self.notificationType == wakeToTimeout) {
141150
self.deliver()
@@ -147,14 +156,19 @@ class WakeNodeWorker @AssistedInject constructor(
147156
}
148157

149158
private suspend fun onChannelClosed(event: Event.ChannelClosed) {
150-
self.bestAttemptContent?.title = "Channel closed"
151-
self.bestAttemptContent?.body = "Reason: ${event.reason}"
152-
153-
if (self.notificationType == mutualClose) {
154-
self.bestAttemptContent?.body = "Balance moved from spending to savings"
155-
} else if (self.notificationType == orderPaymentConfirmed) {
156-
self.bestAttemptContent?.title = "Channel failed to open in the background"
157-
self.bestAttemptContent?.body = "Please try again"
159+
self.bestAttemptContent = when (self.notificationType) {
160+
mutualClose -> NotificationDetails(
161+
title = appContext.getString(R.string.notification_channel_closed_title),
162+
body = appContext.getString(R.string.notification_channel_closed_mutual_body),
163+
)
164+
orderPaymentConfirmed -> NotificationDetails(
165+
title = appContext.getString(R.string.notification_channel_open_bg_failed_title),
166+
body = appContext.getString(R.string.notification_please_try_again_body),
167+
)
168+
else -> NotificationDetails(
169+
title = appContext.getString(R.string.notification_channel_closed_title),
170+
body = appContext.getString(R.string.notification_channel_closed_reason_body, event.reason),
171+
)
158172
}
159173

160174
self.deliver()
@@ -163,22 +177,23 @@ class WakeNodeWorker @AssistedInject constructor(
163177
private suspend fun onPaymentReceived(
164178
event: Event.PaymentReceived,
165179
showDetails: Boolean,
166-
openBitkitMessage: String,
180+
hiddenBody: String,
167181
) {
168-
bestAttemptContent?.title = "Payment Received"
169182
val sats = event.amountMsat / 1000u
170183
// Save for UI to pick up
171-
NewTransactionSheetDetails.save(
172-
appContext,
184+
cacheStore.setBackgroundReceive(
173185
NewTransactionSheetDetails(
174186
type = NewTransactionSheetType.LIGHTNING,
175187
direction = NewTransactionSheetDirection.RECEIVED,
176188
paymentHashOrTxId = event.paymentHash,
177189
sats = sats.toLong(),
178190
)
179191
)
180-
val content = if (showDetails) "$BITCOIN_SYMBOL $sats" else openBitkitMessage
181-
bestAttemptContent?.body = content
192+
val content = if (showDetails) "$BITCOIN_SYMBOL $sats" else hiddenBody
193+
bestAttemptContent = NotificationDetails(
194+
title = appContext.getString(R.string.notification_received_title),
195+
body = content,
196+
)
182197
if (self.notificationType == incomingHtlc) {
183198
self.deliver()
184199
}
@@ -187,21 +202,26 @@ class WakeNodeWorker @AssistedInject constructor(
187202
private suspend fun onChannelReady(
188203
event: Event.ChannelReady,
189204
showDetails: Boolean,
190-
openBitkitMessage: String,
205+
hiddenBody: String,
191206
) {
207+
val viaNewChannel = appContext.getString(R.string.notification_via_new_channel_body)
192208
if (self.notificationType == cjitPaymentArrived) {
193-
self.bestAttemptContent?.title = "Payment received"
194-
self.bestAttemptContent?.body = "Via new channel"
209+
self.bestAttemptContent = NotificationDetails(
210+
title = appContext.getString(R.string.notification_received_title),
211+
body = viaNewChannel,
212+
)
195213

196214
lightningRepo.getChannels()?.find { it.channelId == event.channelId }?.let { channel ->
197215
val sats = channel.amountOnClose
198-
val content = if (showDetails) "$BITCOIN_SYMBOL $sats" else openBitkitMessage
199-
self.bestAttemptContent?.title = content
216+
val content = if (showDetails) "$BITCOIN_SYMBOL $sats" else hiddenBody
217+
self.bestAttemptContent = NotificationDetails(
218+
title = content,
219+
body = viaNewChannel,
220+
)
200221
val cjitEntry = channel.let { blocktankRepo.getCjitEntry(it) }
201222
if (cjitEntry != null) {
202223
// Save for UI to pick up
203-
NewTransactionSheetDetails.save(
204-
appContext,
224+
cacheStore.setBackgroundReceive(
205225
NewTransactionSheetDetails(
206226
type = NewTransactionSheetType.LIGHTNING,
207227
direction = NewTransactionSheetDirection.RECEIVED,
@@ -212,8 +232,10 @@ class WakeNodeWorker @AssistedInject constructor(
212232
}
213233
}
214234
} else if (self.notificationType == orderPaymentConfirmed) {
215-
self.bestAttemptContent?.title = "Channel opened"
216-
self.bestAttemptContent?.body = "Ready to send"
235+
self.bestAttemptContent = NotificationDetails(
236+
title = appContext.getString(R.string.notification_channel_opened_title),
237+
body = appContext.getString(R.string.notification_channel_ready_body),
238+
)
217239
}
218240
self.deliver()
219241
}
@@ -222,7 +244,7 @@ class WakeNodeWorker @AssistedInject constructor(
222244
lightningRepo.stop()
223245

224246
bestAttemptContent?.run {
225-
pushNotification(title, body, context = appContext)
247+
appContext.pushNotification(title, body)
226248
Logger.info("Delivered notification")
227249
}
228250

0 commit comments

Comments
 (0)