Skip to content

Commit 62e9adc

Browse files
jakubuidclaude
andcommitted
fix: prevent payment success modal from getting stuck after back/forth navigation
When navigating back and forth in the payment flow before completing a payment, the success modal would not dismiss on "Got it" or X click. Root causes: 1. Missing launchSingleTop on payment navigation caused duplicate dialog entries to stack on the backstack 2. popBackStack(Route.Connections.path) could silently fail if the backstack was in an unexpected state, leaving the dialog visible 3. SharedFlow replay=1 leaked stale payment data to duplicate ViewModel instances Fixes: WCP4-41 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ece90a5 commit 62e9adc

File tree

3 files changed

+23
-11
lines changed

3 files changed

+23
-11
lines changed

sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/WalletKitActivity.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,9 @@ class WalletKitActivity : AppCompatActivity() {
187187
.onEach { paymentLink ->
188188
navigateWhenReady {
189189
val encodedLink = URLEncoder.encode(paymentLink, "UTF-8")
190-
navController.navigate("${Route.Payment.path}/$encodedLink")
190+
navController.navigate("${Route.Payment.path}/$encodedLink") {
191+
launchSingleTop = true
192+
}
191193
}
192194
}
193195
.launchIn(lifecycleScope)
@@ -209,7 +211,9 @@ class WalletKitActivity : AppCompatActivity() {
209211
lifecycleScope.launch {
210212
navigateWhenReady {
211213
val encodedLink = URLEncoder.encode(dataString, "UTF-8")
212-
navController.navigate("${Route.Payment.path}/$encodedLink")
214+
navController.navigate("${Route.Payment.path}/$encodedLink") {
215+
launchSingleTop = true
216+
}
213217
}
214218
}
215219
return

sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/payment/PaymentRoute.kt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ fun PaymentRoute(
133133
onWhyInfoRequired = { viewModel.showWhyInfoRequired() },
134134
onClose = {
135135
viewModel.cancel()
136-
navController.popBackStack(Route.Connections.path, inclusive = false)
136+
dismissPaymentDialog(navController)
137137
}
138138
)
139139
}
@@ -148,7 +148,7 @@ fun PaymentRoute(
148148
},
149149
onClose = {
150150
viewModel.cancel()
151-
navController.popBackStack(Route.Connections.path, inclusive = false)
151+
dismissPaymentDialog(navController)
152152
}
153153
)
154154
}
@@ -159,7 +159,7 @@ fun PaymentRoute(
159159
onConfirm = { viewModel.confirmFromSummary() },
160160
onClose = {
161161
viewModel.cancel()
162-
navController.popBackStack(Route.Connections.path, inclusive = false)
162+
dismissPaymentDialog(navController)
163163
}
164164
)
165165
}
@@ -168,7 +168,7 @@ fun PaymentRoute(
168168
onBack = { viewModel.dismissWhyInfoRequired() },
169169
onClose = {
170170
viewModel.cancel()
171-
navController.popBackStack(Route.Connections.path, inclusive = false)
171+
dismissPaymentDialog(navController)
172172
}
173173
)
174174
}
@@ -177,7 +177,7 @@ fun PaymentRoute(
177177
message = state.message,
178178
onClose = {
179179
viewModel.cancel()
180-
navController.popBackStack(Route.Connections.path, inclusive = false)
180+
dismissPaymentDialog(navController)
181181
}
182182
)
183183
}
@@ -187,13 +187,13 @@ fun PaymentRoute(
187187
onDone = {
188188
viewModel.cancel()
189189
onPaymentSuccess()
190-
navController.popBackStack(Route.Connections.path, inclusive = false)
190+
dismissPaymentDialog(navController)
191191
Toast.makeText(context, "Payment successful!", Toast.LENGTH_SHORT).show()
192192
},
193193
onClose = {
194194
viewModel.cancel()
195195
onPaymentSuccess()
196-
navController.popBackStack(Route.Connections.path, inclusive = false)
196+
dismissPaymentDialog(navController)
197197
}
198198
)
199199
}
@@ -202,11 +202,11 @@ fun PaymentRoute(
202202
message = state.message,
203203
onRetry = {
204204
viewModel.cancel()
205-
navController.popBackStack(Route.Connections.path, inclusive = false)
205+
dismissPaymentDialog(navController)
206206
},
207207
onClose = {
208208
viewModel.cancel()
209-
navController.popBackStack(Route.Connections.path, inclusive = false)
209+
dismissPaymentDialog(navController)
210210
}
211211
)
212212
}
@@ -218,6 +218,12 @@ fun PaymentRoute(
218218
}
219219
}
220220

221+
private fun dismissPaymentDialog(navController: NavHostController) {
222+
if (!navController.popBackStack(Route.Connections.path, inclusive = false)) {
223+
navController.popBackStack()
224+
}
225+
}
226+
221227
@Composable
222228
private fun LoadingContent() {
223229
Column(

sample/wallet/src/main/kotlin/com/reown/sample/wallet/ui/routes/dialog_routes/payment/PaymentViewModel.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class PaymentViewModel : ViewModel() {
6161
* Always goes directly to Options state (no Intro screen).
6262
*/
6363
private fun processPaymentOptionsResponse(response: Wallet.Model.PaymentOptionsResponse) {
64+
// Clear replay cache immediately after consuming to prevent stale data leaking to other ViewModel instances
65+
WalletKitDelegate.clearPaymentOptions()
6466
currentPaymentId = response.paymentId
6567
collectedValues.clear()
6668
currentFieldIndex = 0

0 commit comments

Comments
 (0)