What changed, and why it matters
This commit is a routine UI cleanup. It removes a hidden developer-only feature that let testers manually override CakePay order statuses on their own device, and replaces several copy-pasted loading spinner widgets with a single shared loading widget. There is no indication this was a security fix or that the removed feature could be exploited by normal users.
No security action required. Treat as normal code hygiene. If the project wants to retain the developer override capability for QA, it should be gated behind a build-time debug flag rather than a hidden settings dialog.
Security signals we found
Removal of developer-only status override map and hidden debug dialog
No changes to API client, authentication, encryption, or transaction logic
Pure UI refactor: centralizes loading indicator widget usage
Evidence from the diff
The diff removes CakePayService.devStatusOverrides, a static in-memory map used only by a hidden settings dialog (_CakePayDevStatusDialog) to locally override order statuses for UI testing. It also removes the dialog itself and the per-order override logic in cakepay_order_view.dart and cakepay_orders_view.dart. The remaining changes refactor inline CircularProgressIndicator usages into a new LoadingIndicator widget across CakePay, ShopInBit, and exchange UI pages. No network, crypto, or authentication code is changed.
Changed components
lib/services/cakepay/cakepay_service.dartlib/pages/settings_views/global_settings_view/hidden_settings.dartlib/pages/cakepay/cakepay_order_view.dartlib/pages/cakepay/cakepay_orders_view.dartlib/pages/shopinbit/shopinbit_offer_view.dartlib/pages/shopinbit/shopinbit_payment_view.dartlib/pages/shopinbit/shopinbit_ticket_detail.dartlib/pages/shopinbit/shopinbit_tickets_view.dartlib/pages/exchange_view/sub_widgets/exchange_provider_option.dartInspect captured patch +50 / −285
diff --git a/lib/pages/cakepay/cakepay_order_view.dart b/lib/pages/cakepay/cakepay_order_view.dart
index 71c9fe8..aa11d29 100644
--- a/lib/pages/cakepay/cakepay_order_view.dart
+++ b/lib/pages/cakepay/cakepay_order_view.dart
@@ -23,6 +23,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
+import '../../widgets/loading_indicator.dart';
import '../../widgets/qr.dart';
import '../../widgets/rounded_white_container.dart';
import 'cakepay_send_from_view.dart';
@@ -215,12 +216,7 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
setState(() {
_loading = false;
if (!resp.hasError && resp.value != null) {
- var order = resp.value!;
- final override = CakePayService.devStatusOverrides[order.orderId];
- if (override != null) {
- order = order.copyWith(status: override);
- }
- _order = order;
+ _order = resp.value!;
if (_isTerminal(_order!.status)) {
_pollTimer?.cancel();
_countdownTimer?.cancel();
@@ -324,60 +320,6 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
];
}
- String _statusLabel(CakePayOrderStatus status) {
- switch (status) {
- case CakePayOrderStatus.new_:
- return "New";
- case CakePayOrderStatus.expiredButStillPending:
- return "Expired (pending)";
- case CakePayOrderStatus.expired:
- return "Expired";
- case CakePayOrderStatus.failed:
- return "Failed";
- case CakePayOrderStatus.paid:
- return "Paid";
- case CakePayOrderStatus.paidPartial:
- return "Partially paid";
- case CakePayOrderStatus.pendingPurchase:
- return "Pending purchase";
- case CakePayOrderStatus.purchaseProcessing:
- return "Processing";
- case CakePayOrderStatus.purchased:
- return "Purchased";
- case CakePayOrderStatus.pendingEmail:
- return "Pending email";
- case CakePayOrderStatus.complete:
- return "Complete";
- case CakePayOrderStatus.pendingRefund:
- return "Pending refund";
- case CakePayOrderStatus.refunded:
- return "Refunded";
- }
- }
-
- Color _statusColor(BuildContext context, CakePayOrderStatus status) {
- final colors = Theme.of(context).extension<StackColors>()!;
- switch (status) {
- case CakePayOrderStatus.complete:
- case CakePayOrderStatus.purchased:
- return colors.accentColorGreen;
- case CakePayOrderStatus.new_:
- case CakePayOrderStatus.paid:
- case CakePayOrderStatus.paidPartial:
- return colors.accentColorBlue;
- case CakePayOrderStatus.pendingPurchase:
- case CakePayOrderStatus.purchaseProcessing:
- case CakePayOrderStatus.pendingEmail:
- case CakePayOrderStatus.expiredButStillPending:
- return colors.accentColorYellow;
- case CakePayOrderStatus.expired:
- case CakePayOrderStatus.failed:
- case CakePayOrderStatus.pendingRefund:
- case CakePayOrderStatus.refunded:
- return colors.textSubtitle1;
- }
- }
-
@override
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
@@ -385,13 +327,7 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
if (_loading) {
return _scaffold(
isDesktop: isDesktop,
- child: const Center(
- child: SizedBox(
- width: 24,
- height: 24,
- child: CircularProgressIndicator(strokeWidth: 2),
- ),
- ),
+ child: const LoadingIndicator(width: 24, height: 24),
);
}
@@ -412,24 +348,33 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
final order = _order!;
final paymentOptions = order.paymentOptions;
- final statusBadge = Container(
- padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(8),
- color: _statusColor(context, order.status).withValues(alpha: 0.2),
- ),
- child: Text(
- _statusLabel(order.status),
- style:
- (isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(context)
- : STextStyles.itemSubtitle12(context))
- .copyWith(color: _statusColor(context, order.status)),
- ),
- );
-
final details = <Widget>[
- Row(mainAxisAlignment: MainAxisAlignment.end, children: [statusBadge]),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ color: order.status
+ .color(Theme.of(context).extension<StackColors>()!)
+ .withValues(alpha: 0.2),
+ ),
+ child: Text(
+ order.status.label,
+ style:
+ (isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.itemSubtitle12(context))
+ .copyWith(
+ color: order.status.color(
+ Theme.of(context).extension<StackColors>()!,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
SizedBox(height: isDesktop ? 8 : 6),
RoundedWhiteContainer(
child: GestureDetector(
@@ -727,7 +672,7 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
const SizedBox(width: 8),
Expanded(
child: Text(
- _statusLabel(status),
+ status.label,
style:
(isDesktop
? STextStyles.desktopTextExtraExtraSmall(context)
diff --git a/lib/pages/cakepay/cakepay_orders_view.dart b/lib/pages/cakepay/cakepay_orders_view.dart
index e1fd135..3f52d5a 100644
--- a/lib/pages/cakepay/cakepay_orders_view.dart
+++ b/lib/pages/cakepay/cakepay_orders_view.dart
@@ -10,6 +10,7 @@ import '../../widgets/conditional_parent.dart';
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
+import '../../widgets/loading_indicator.dart';
import '../../widgets/rounded_white_container.dart';
import 'cakepay_order_view.dart';
@@ -44,12 +45,7 @@ class _CakePayOrdersViewState extends State<CakePayOrdersView> {
for (final id in orderIds) {
final resp = await CakePayService.instance.client.getOrder(id);
if (!resp.hasError && resp.value != null) {
- var order = resp.value!;
- final override = CakePayService.devStatusOverrides[order.orderId];
- if (override != null) {
- order = order.copyWith(status: override);
- }
- results.add(order);
+ results.add(resp.value!);
}
}
@@ -193,14 +189,7 @@ class _CakePayOrdersViewState extends State<CakePayOrdersView> {
final content = Stack(
children: [
list,
- if (_syncing)
- const Center(
- child: SizedBox(
- width: 24,
- height: 24,
- child: CircularProgressIndicator(strokeWidth: 2),
- ),
- ),
+ if (_syncing) const LoadingIndicator(width: 24, height: 24),
],
);
diff --git a/lib/pages/exchange_view/sub_widgets/exchange_provider_option.dart b/lib/pages/exchange_view/sub_widgets/exchange_provider_option.dart
index 1a3a88d..7000886 100644
--- a/lib/pages/exchange_view/sub_widgets/exchange_provider_option.dart
+++ b/lib/pages/exchange_view/sub_widgets/exchange_provider_option.dart
@@ -36,6 +36,7 @@ import '../../../widgets/dialogs/basic_dialog.dart';
import '../../../widgets/exchange/trocador/trocador_kyc_info_button.dart';
import '../../../widgets/exchange/trocador/trocador_rating_type_enum.dart';
import '../../../widgets/icon_widgets/exchange_icon.dart';
+import '../../../widgets/loading_indicator.dart';
class ExchangeOption extends ConsumerStatefulWidget {
const ExchangeOption({
@@ -388,9 +389,7 @@ class _ProviderOptionState extends ConsumerState<ExchProviderOption> {
if (loadingProgress == null) {
return child;
} else {
- return const Center(
- child: CircularProgressIndicator(),
- );
+ return const LoadingIndicator();
}
},
errorBuilder: (context, error, stackTrace) {
diff --git a/lib/pages/settings_views/global_settings_view/hidden_settings.dart b/lib/pages/settings_views/global_settings_view/hidden_settings.dart
index ca102c4..46ab31b 100644
--- a/lib/pages/settings_views/global_settings_view/hidden_settings.dart
+++ b/lib/pages/settings_views/global_settings_view/hidden_settings.dart
@@ -17,8 +17,6 @@ import 'package:flutter_svg/flutter_svg.dart';
import '../../../db/isar/main_db.dart';
import '../../../notifications/show_flush_bar.dart';
import '../../../providers/providers.dart';
-import '../../../services/cakepay/cakepay_service.dart';
-import '../../../services/cakepay/src/models/order.dart';
import '../../../themes/stack_colors.dart';
import '../../../utilities/assets.dart';
import '../../../utilities/constants.dart';
@@ -369,25 +367,6 @@ class HiddenSettings extends StatelessWidget {
);
},
),
- const SizedBox(height: 12),
- GestureDetector(
- onTap: () {
- showDialog<void>(
- context: context,
- builder: (_) => const _CakePayDevStatusDialog(),
- );
- },
- child: RoundedWhiteContainer(
- child: Text(
- "CakePay status overrides",
- style: STextStyles.button(context).copyWith(
- color: Theme.of(
- context,
- ).extension<StackColors>()!.accentColorDark,
- ),
- ),
- ),
- ),
// const SizedBox(
// height: 12,
// ),
@@ -428,124 +407,3 @@ class HiddenSettings extends StatelessWidget {
);
}
}
-
-class _CakePayDevStatusDialog extends StatefulWidget {
- const _CakePayDevStatusDialog();
-
- @override
- State<_CakePayDevStatusDialog> createState() =>
- _CakePayDevStatusDialogState();
-}
-
-class _CakePayDevStatusDialogState extends State<_CakePayDevStatusDialog> {
- late final List<String> _orderIds;
-
- @override
- void initState() {
- super.initState();
- _orderIds = CakePayService.instance.getOrderIds();
- }
-
- @override
- Widget build(BuildContext context) {
- final colors = Theme.of(context).extension<StackColors>()!;
-
- return AlertDialog(
- title: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "CakePay Status Overrides",
- style: STextStyles.pageTitleH2(context),
- ),
- if (CakePayService.devStatusOverrides.isNotEmpty)
- TextButton(
- onPressed: () {
- setState(() {
- CakePayService.devStatusOverrides.clear();
- });
- },
- child: Text("Clear all", style: STextStyles.link2(context)),
- ),
- ],
- ),
- content: SizedBox(
- width: 400,
- child: _orderIds.isEmpty
- ? Text(
- "No tracked CakePay orders.\n"
- "Create an order first, then come back here to override "
- "its status.",
- style: STextStyles.itemSubtitle(context),
- )
- : ListView.separated(
- shrinkWrap: true,
- itemCount: _orderIds.length,
- separatorBuilder: (_, __) => const Divider(height: 16),
- itemBuilder: (context, index) {
- final id = _orderIds[index];
- final current = CakePayService.devStatusOverrides[id];
-
- return Row(
- children: [
- Expanded(
- child: Text(
- id.length > 12 ? "${id.substring(0, 12)}..." : id,
- style: STextStyles.itemSubtitle12(context),
- ),
- ),
- const SizedBox(width: 8),
- DropdownButton<CakePayOrderStatus?>(
- value: current,
- hint: Text(
- "API default",
- style: STextStyles.itemSubtitle12(
- context,
- ).copyWith(color: colors.textSubtitle2),
- ),
- underline: const SizedBox(),
- isDense: true,
- items: [
- DropdownMenuItem<CakePayOrderStatus?>(
- value: null,
- child: Text(
- "API default",
- style: STextStyles.itemSubtitle12(
- context,
- ).copyWith(color: colors.textSubtitle2),
- ),
- ),
- ...CakePayOrderStatus.values.map(
- (s) => DropdownMenuItem(
- value: s,
- child: Text(
- s.value,
- style: STextStyles.itemSubtitle12(context),
- ),
- ),
- ),
- ],
- onChanged: (value) {
- setState(() {
- if (value == null) {
- CakePayService.devStatusOverrides.remove(id);
- } else {
- CakePayService.devStatusOverrides[id] = value;
- }
- });
- },
- ),
- ],
- );
- },
- ),
- ),
- actions: [
- TextButton(
- onPressed: () => Navigator.of(context).pop(),
- child: Text("Close", style: STextStyles.button(context)),
- ),
- ],
- );
- }
-}
diff --git a/lib/pages/shopinbit/shopinbit_offer_view.dart b/lib/pages/shopinbit/shopinbit_offer_view.dart
index ace2f3d..f746b03 100644
--- a/lib/pages/shopinbit/shopinbit_offer_view.dart
+++ b/lib/pages/shopinbit/shopinbit_offer_view.dart
@@ -11,6 +11,7 @@ import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
import '../../widgets/desktop/secondary_button.dart';
+import '../../widgets/loading_indicator.dart';
import '../../widgets/rounded_white_container.dart';
import 'shopinbit_shipping_view.dart';
@@ -154,14 +155,6 @@ class _ShopInBitOfferViewState extends State<ShopInBitOfferView> {
],
);
- const loadingOverlay = Center(
- child: SizedBox(
- width: 24,
- height: 24,
- child: CircularProgressIndicator(strokeWidth: 2),
- ),
- );
-
if (isDesktop) {
return DesktopDialog(
maxWidth: 580,
@@ -187,7 +180,12 @@ class _ShopInBitOfferViewState extends State<ShopInBitOfferView> {
horizontal: 32,
vertical: 16,
),
- child: Stack(children: [content, if (_loading) loadingOverlay]),
+ child: Stack(
+ children: [
+ content,
+ if (_loading) const LoadingIndicator(width: 24, height: 24),
+ ],
+ ),
),
),
],
@@ -220,7 +218,7 @@ class _ShopInBitOfferViewState extends State<ShopInBitOfferView> {
),
),
),
- if (_loading) loadingOverlay,
+ if (_loading) const LoadingIndicator(width: 24, height: 24),
],
);
},
diff --git a/lib/pages/shopinbit/shopinbit_payment_view.dart b/lib/pages/shopinbit/shopinbit_payment_view.dart
index 0467d3f..9813669 100644
--- a/lib/pages/shopinbit/shopinbit_payment_view.dart
+++ b/lib/pages/shopinbit/shopinbit_payment_view.dart
@@ -29,6 +29,7 @@ import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
import '../../widgets/desktop/secondary_button.dart';
+import '../../widgets/loading_indicator.dart';
import '../../widgets/rounded_white_container.dart';
import 'shopinbit_send_from_view.dart';
@@ -471,14 +472,6 @@ class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
- const loadingOverlay = Center(
- child: SizedBox(
- width: 24,
- height: 24,
- child: CircularProgressIndicator(strokeWidth: 2),
- ),
- );
-
// Build coin rows from _methods/_addresses
final coinRows = <Widget>[];
for (int i = 0; i < _methods.length; i++) {
@@ -737,7 +730,7 @@ class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
child: Stack(
children: [
SingleChildScrollView(child: content),
- if (_loading) loadingOverlay,
+ if (_loading) const LoadingIndicator(width: 24, height: 24),
],
),
),
@@ -779,7 +772,7 @@ class _ShopInBitPaymentViewState extends ConsumerState<ShopInBitPaymentView> {
),
),
),
- if (_loading) loadingOverlay,
+ if (_loading) const LoadingIndicator(width: 24, height: 24),
],
);
},
diff --git a/lib/pages/shopinbit/shopinbit_ticket_detail.dart b/lib/pages/shopinbit/shopinbit_ticket_detail.dart
index 85ceb97..1a77816 100644
--- a/lib/pages/shopinbit/shopinbit_ticket_detail.dart
+++ b/lib/pages/shopinbit/shopinbit_ticket_detail.dart
@@ -15,6 +15,7 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
+import '../../widgets/loading_indicator.dart';
import '../../widgets/rounded_white_container.dart';
import 'shopinbit_offer_view.dart';
@@ -497,14 +498,7 @@ class _ShopInBitTicketDetailState extends State<ShopInBitTicketDetail> {
return _chatBubble(message, isDesktop);
},
),
- if (_loading)
- const Center(
- child: SizedBox(
- width: 24,
- height: 24,
- child: CircularProgressIndicator(strokeWidth: 2),
- ),
- ),
+ if (_loading) const LoadingIndicator(width: 24, height: 24),
],
),
);
diff --git a/lib/pages/shopinbit/shopinbit_tickets_view.dart b/lib/pages/shopinbit/shopinbit_tickets_view.dart
index ce62d3b..d600a00 100644
--- a/lib/pages/shopinbit/shopinbit_tickets_view.dart
+++ b/lib/pages/shopinbit/shopinbit_tickets_view.dart
@@ -15,6 +15,7 @@ import '../../widgets/background.dart';
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
+import '../../widgets/loading_indicator.dart';
import '../../widgets/rounded_white_container.dart';
import 'shopinbit_car_fee_view.dart';
import 'shopinbit_car_research_payment_view.dart';
@@ -446,14 +447,7 @@ class _ShopInBitTicketsViewState extends State<ShopInBitTicketsView> {
final content = Stack(
children: [
list,
- if (_syncing)
- const Center(
- child: SizedBox(
- width: 24,
- height: 24,
- child: CircularProgressIndicator(strokeWidth: 2),
- ),
- ),
+ if (_syncing) const LoadingIndicator(width: 24, height: 24),
],
);
diff --git a/lib/services/cakepay/cakepay_service.dart b/lib/services/cakepay/cakepay_service.dart
index 1016bc4..86c4930 100644
--- a/lib/services/cakepay/cakepay_service.dart
+++ b/lib/services/cakepay/cakepay_service.dart
@@ -1,16 +1,11 @@
import '../../db/hive/db.dart';
import '../../external_api_keys.dart';
import 'src/client.dart';
-import 'src/models/order.dart';
class CakePayService {
static final instance = CakePayService._();
CakePayService._();
- /// Dev-only: override order statuses for local UI testing.
- /// Keys are order IDs, values are the status to pretend the API returned.
- static final Map<String, CakePayOrderStatus> devStatusOverrides = {};
-
CakePayClient? _client;
CakePayClient get client {
Why this scored 16/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.