Skip to content
This repository was archived by the owner on Oct 17, 2025. It is now read-only.

Commit 8f1bcd3

Browse files
authored
refactor: unify android error reporting (#5)
## Summary Convert OpenIapError variants to object singletons with shared message constants. Rename the purchase request type to RequestPurchaseParams and update protocols, store flows, and tests to match. Update the sample UI to surface purchase status banners with PurchaseResultStatus and refresh cSpell dictionary entries.
1 parent df15b1a commit 8f1bcd3

File tree

16 files changed

+459
-403
lines changed

16 files changed

+459
-403
lines changed

.vscode/settings.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,9 @@
3939
"typescript.validate.enable": false,
4040
"javascript.validate.enable": false,
4141
"typescript.tsc.autoDetect": "off",
42-
"npm.autoDetect": "off"
43-
}
42+
"npm.autoDetect": "off",
43+
"cSpell.words": [
44+
"billingclient",
45+
"openiap"
46+
]
47+
}

Example/src/main/java/dev/hyo/martie/screens/AvailablePurchasesScreen.kt

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import androidx.compose.foundation.lazy.LazyColumn
77
import androidx.compose.foundation.lazy.items
88
import androidx.compose.foundation.shape.RoundedCornerShape
99
import androidx.compose.material.icons.Icons
10+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
1011
import androidx.compose.material.icons.filled.*
1112
import androidx.compose.material3.*
1213
import androidx.compose.runtime.*
@@ -22,6 +23,7 @@ import dev.hyo.martie.screens.uis.*
2223
import dev.hyo.openiap.IapContext
2324
import dev.hyo.openiap.store.OpenIapStore
2425
import dev.hyo.openiap.models.*
26+
import dev.hyo.openiap.store.PurchaseResultStatus
2527
import kotlinx.coroutines.launch
2628

2729
@OptIn(ExperimentalMaterial3Api::class)
@@ -36,11 +38,7 @@ fun AvailablePurchasesScreen(
3638
val purchases by iapStore.availablePurchases.collectAsState()
3739
val status by iapStore.status.collectAsState()
3840
val connectionStatus by iapStore.connectionStatus.collectAsState()
39-
40-
var showRestoreResult by remember { mutableStateOf(false) }
41-
var restoreResultMessage by remember { mutableStateOf("") }
42-
var showError by remember { mutableStateOf(false) }
43-
var errorMessage by remember { mutableStateOf("") }
41+
val statusMessage = status.lastPurchaseResult
4442

4543
// Modal state
4644
var selectedPurchase by remember { mutableStateOf<OpenIapPurchase?>(null) }
@@ -67,7 +65,7 @@ fun AvailablePurchasesScreen(
6765
title = { Text("Available Purchases") },
6866
navigationIcon = {
6967
IconButton(onClick = { navController.navigateUp() }) {
70-
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
68+
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
7169
}
7270
},
7371
actions = {
@@ -77,11 +75,15 @@ fun AvailablePurchasesScreen(
7775
scope.launch {
7876
try {
7977
val restored = iapStore.restorePurchases()
80-
showRestoreResult = true
81-
restoreResultMessage = "Restored ${restored.size} purchases"
78+
iapStore.postStatusMessage(
79+
message = "Restored ${restored.size} purchases",
80+
status = PurchaseResultStatus.SUCCESS
81+
)
8282
} catch (e: Exception) {
83-
showError = true
84-
errorMessage = e.message ?: "Restore failed"
83+
iapStore.postStatusMessage(
84+
message = e.message ?: "Restore failed",
85+
status = PurchaseResultStatus.ERROR
86+
)
8587
}
8688
}
8789
},
@@ -161,12 +163,12 @@ fun AvailablePurchasesScreen(
161163
}
162164
}
163165

164-
// Restore Result
165-
if (showRestoreResult) {
166-
item {
166+
statusMessage?.let { result ->
167+
item("status-message") {
167168
PurchaseResultCard(
168-
message = restoreResultMessage,
169-
onDismiss = { showRestoreResult = false }
169+
message = result.message,
170+
status = result.status,
171+
onDismiss = { iapStore.clearStatusMessage() }
170172
)
171173
}
172174
}
@@ -214,16 +216,25 @@ fun AvailablePurchasesScreen(
214216
try {
215217
val ok = iapStore.finishTransaction(purchase, isConsumable)
216218
if (ok) {
217-
showRestoreResult = true
218-
restoreResultMessage = "Transaction finished successfully"
219+
iapStore.postStatusMessage(
220+
message = "Transaction finished successfully",
221+
status = PurchaseResultStatus.SUCCESS,
222+
productId = purchase.productId
223+
)
219224
iapStore.getAvailablePurchases()
220225
} else {
221-
showError = true
222-
errorMessage = "Failed to finish transaction"
226+
iapStore.postStatusMessage(
227+
message = "Failed to finish transaction",
228+
status = PurchaseResultStatus.ERROR,
229+
productId = purchase.productId
230+
)
223231
}
224232
} catch (e: Exception) {
225-
showError = true
226-
errorMessage = e.message ?: "Failed to finish transaction"
233+
iapStore.postStatusMessage(
234+
message = e.message ?: "Failed to finish transaction",
235+
status = PurchaseResultStatus.ERROR,
236+
productId = purchase.productId
237+
)
227238
}
228239
}
229240
},
@@ -399,11 +410,15 @@ fun AvailablePurchasesScreen(
399410
scope.launch {
400411
try {
401412
val restored = iapStore.restorePurchases()
402-
showRestoreResult = true
403-
restoreResultMessage = "Restored ${restored.size} purchases"
413+
iapStore.postStatusMessage(
414+
message = "Restored ${restored.size} purchases",
415+
status = PurchaseResultStatus.SUCCESS
416+
)
404417
} catch (e: Exception) {
405-
showError = true
406-
errorMessage = e.message ?: "Restore failed"
418+
iapStore.postStatusMessage(
419+
message = e.message ?: "Restore failed",
420+
status = PurchaseResultStatus.ERROR
421+
)
407422
}
408423
}
409424
},
@@ -419,20 +434,6 @@ fun AvailablePurchasesScreen(
419434
}
420435
}
421436

422-
// Error Dialog
423-
if (showError) {
424-
AlertDialog(
425-
onDismissRequest = { showError = false },
426-
title = { Text("Error") },
427-
text = { Text(errorMessage) },
428-
confirmButton = {
429-
TextButton(onClick = { showError = false }) {
430-
Text("OK")
431-
}
432-
}
433-
)
434-
}
435-
436437
// Purchase Detail Modal
437438
selectedPurchase?.let { purchase ->
438439
PurchaseDetailModal(

Example/src/main/java/dev/hyo/martie/screens/OfferCodeScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.compose.foundation.background
44
import androidx.compose.foundation.layout.*
55
import androidx.compose.foundation.shape.RoundedCornerShape
66
import androidx.compose.material.icons.Icons
7+
import androidx.compose.material.icons.automirrored.filled.ArrowBack
78
import androidx.compose.material.icons.filled.*
89
import androidx.compose.material3.*
910
import androidx.compose.runtime.*
@@ -48,7 +49,7 @@ fun OfferCodeScreen(
4849
title = { Text("Offer Code") },
4950
navigationIcon = {
5051
IconButton(onClick = { navController.navigateUp() }) {
51-
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
52+
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
5253
}
5354
},
5455
colors = TopAppBarDefaults.topAppBarColors(

0 commit comments

Comments
 (0)