fix(ui): cakepay desktop navigation mostly
What changed, and why it matters
This commit is a user-interface cleanup for the CakePay gift-card flow on desktop. It replaces separate mobile/desktop navigation paths with a single nested navigator inside a dialog, swaps some loading indicators, and changes how order details receive their data (passing the whole order object instead of just an order ID). There is no clear security bug being fixed, and nothing in the diff suggests attackers could exploit it.
No security action required. Treat as a normal UI/UX refactor. If reviewing further, verify that the new route argument types (CakePayOrder, CakePayCard) are always constructed from trusted API responses and not from untrusted user input, but the diff itself does not introduce such a path.
Security signals we found
No security-relevant keywords in commit title or message
No changes to crypto, wallet, authentication, or network trust boundaries
Argument-type validation added in route generators (defensive, not a vulnerability fix)
UI-only refactor with no new permissions or external dependencies
Evidence from the diff
The patch refactors CakePay routing and presentation: (1) desktop no longer pops the current dialog and opens a new one; instead it pushes routes inside a NestedNavigatorDialog; (2) CakePayOrderView now takes a CakePayOrder object rather than a String orderId, and route generators validate the argument type; (3) loading states are moved into a shared showLoading helper; (4) some GestureDetector/RoundedWhiteContainer usages are replaced with RoundedContainer + onPressed. No cryptographic, authentication, authorization, or input-validation changes are visible. The commit message frames this as a navigation/UX fix only.
Changed components
lib/pages/cakepay/cakepay_card_detail_view.dartlib/pages/cakepay/cakepay_order_view.dartlib/pages/cakepay/cakepay_orders_view.dartlib/pages/cakepay/cakepay_vendors_view.dartlib/pages/shopinbit/shopinbit_tickets_view.dartlib/pages_desktop_specific/services/cakepay/desktop_gift_cards_view.dartlib/route_generator.dartlib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dartInspect captured patch +362 / −377
diff --git a/lib/pages/cakepay/cakepay_card_detail_view.dart b/lib/pages/cakepay/cakepay_card_detail_view.dart
index 07ec3d6..0121b24 100644
--- a/lib/pages/cakepay/cakepay_card_detail_view.dart
+++ b/lib/pages/cakepay/cakepay_card_detail_view.dart
@@ -193,18 +193,9 @@ class _CakePayCardDetailViewState extends State<CakePayCardDetailView> {
await CakePayService.instance.addOrderId(order.orderId);
if (mounted) {
- if (Util.isDesktop) {
- Navigator.of(context, rootNavigator: true).pop();
- await showDialog<void>(
- context: context,
- builder: (_) => CakePayOrderView(orderId: order.orderId),
- );
- } else {
- await Navigator.of(context).pushReplacementNamed(
- CakePayOrderView.routeName,
- arguments: order.orderId,
- );
- }
+ await Navigator.of(
+ context,
+ ).pushReplacementNamed(CakePayOrderView.routeName, arguments: order);
}
} else {
final String errorMessage;
diff --git a/lib/pages/cakepay/cakepay_order_view.dart b/lib/pages/cakepay/cakepay_order_view.dart
index 62fc0bd..7488d0e 100644
--- a/lib/pages/cakepay/cakepay_order_view.dart
+++ b/lib/pages/cakepay/cakepay_order_view.dart
@@ -14,6 +14,7 @@ import '../../services/cakepay/src/models/order.dart';
import '../../themes/stack_colors.dart';
import '../../utilities/amount/amount.dart';
import '../../utilities/assets.dart';
+import '../../utilities/show_loading.dart';
import '../../utilities/text_styles.dart';
import '../../utilities/util.dart';
import '../../wallets/crypto_currency/crypto_currency.dart';
@@ -23,25 +24,24 @@ import '../../widgets/custom_buttons/app_bar_icon_button.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/desktop/primary_button.dart';
import '../../widgets/dialogs/s_dialog.dart';
-import '../../widgets/loading_indicator.dart';
import '../../widgets/qr.dart';
import '../../widgets/rounded_white_container.dart';
+import '../wallet_view/transaction_views/transaction_details_view.dart';
import 'cakepay_send_from_view.dart';
class CakePayOrderView extends ConsumerStatefulWidget {
- const CakePayOrderView({super.key, required this.orderId});
+ const CakePayOrderView({super.key, required this.order});
static const String routeName = "/cakePayOrder";
- final String orderId;
+ final CakePayOrder order;
@override
ConsumerState<CakePayOrderView> createState() => _CakePayOrderViewState();
}
class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
- CakePayOrder? _order;
- bool _loading = true;
+ late CakePayOrder _order;
Timer? _pollTimer;
Timer? _countdownTimer;
Duration _timeRemaining = Duration.zero;
@@ -50,7 +50,11 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
@override
void initState() {
super.initState();
- _loadOrder();
+ _order = widget.order;
+
+ // TODO: _loadOrder already locked up the ui previously, this just puts a
+ // nicer loading ui in place
+ WidgetsBinding.instance.addPostFrameCallback((_) => _loadOrder());
_pollTimer = Timer.periodic(
const Duration(seconds: 15),
(_) => _loadOrder(),
@@ -74,9 +78,9 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
}
void _updateTimeRemaining() {
- if (_order?.expirationTime == null) return;
+ if (_order.expirationTime == null) return;
final expiresAt = DateTime.fromMillisecondsSinceEpoch(
- _order!.expirationTime!,
+ _order.expirationTime!,
);
final remaining = expiresAt.difference(DateTime.now());
if (mounted) {
@@ -107,7 +111,6 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
}) {
final isDesktop = Util.isDesktop;
if (isDesktop) {
- Navigator.of(context, rootNavigator: true).pop();
showDialog<void>(
context: context,
builder: (_) => CakePaySendFromView(
@@ -211,16 +214,26 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
}
Future<void> _loadOrder() async {
- final resp = await CakePayService.instance.client.getOrder(widget.orderId);
+ await showLoading(
+ context: context,
+ message: "Updating order...",
+ whileFutureAlt: _loadOrderHelper,
+ rootNavigator: Util.isDesktop,
+ );
+ }
+
+ Future<void> _loadOrderHelper() async {
+ final resp = await CakePayService.instance.client.getOrder(
+ widget.order.orderId,
+ );
if (mounted) {
setState(() {
- _loading = false;
if (!resp.hasError && resp.value != null) {
_order = resp.value!;
- if (_isTerminal(_order!.status)) {
+ if (_isTerminal(_order.status)) {
_pollTimer?.cancel();
_countdownTimer?.cancel();
- } else if (_order!.expirationTime != null) {
+ } else if (_order.expirationTime != null) {
_startCountdown();
}
}
@@ -266,42 +279,34 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
return [
// Copyable order ID.
RoundedWhiteContainer(
- child: GestureDetector(
- onTap: () {
- Clipboard.setData(ClipboardData(text: order.orderId));
- showFloatingFlushBar(
- type: FlushBarType.info,
- message: "Order ID copied",
- iconAsset: Assets.svg.copy,
- context: context,
- );
- },
- child: Row(
- children: [
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text("Order ID", style: subtitleStyle),
- const SizedBox(height: 4),
- Text(
- order.orderId,
- style: isDesktop
- ? STextStyles.desktopTextSmall(context)
- : STextStyles.titleBold12(context),
- ),
- ],
- ),
- ),
- Icon(
- Icons.copy,
- size: 14,
- color: Theme.of(
- context,
- ).extension<StackColors>()!.accentColorBlue,
+ onPressed: () {
+ Clipboard.setData(ClipboardData(text: order.orderId));
+ showFloatingFlushBar(
+ type: FlushBarType.info,
+ message: "Order ID copied",
+ iconAsset: Assets.svg.copy,
+ context: context,
+ );
+ },
+ child: Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text("Order ID", style: subtitleStyle),
+ const SizedBox(height: 4),
+ Text(
+ order.orderId,
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.titleBold12(context),
+ ),
+ ],
),
- ],
- ),
+ ),
+ IconCopyButton(data: order.orderId),
+ ],
),
),
// Created-at timestamp.
@@ -324,28 +329,7 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
- if (_loading) {
- return _scaffold(
- isDesktop: isDesktop,
- child: const LoadingIndicator(width: 24, height: 24),
- );
- }
-
- if (_order == null) {
- return _scaffold(
- isDesktop: isDesktop,
- child: Center(
- child: Text(
- "Failed to load order",
- style: isDesktop
- ? STextStyles.desktopTextSmall(context)
- : STextStyles.itemSubtitle(context),
- ),
- ),
- );
- }
-
- final order = _order!;
+ final order = _order;
final paymentOptions = order.paymentOptions;
final details = <Widget>[
@@ -377,46 +361,38 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
),
SizedBox(height: isDesktop ? 8 : 6),
RoundedWhiteContainer(
- child: GestureDetector(
- onTap: () {
- Clipboard.setData(ClipboardData(text: order.orderId));
- showFloatingFlushBar(
- type: FlushBarType.info,
- message: "Order ID copied",
- iconAsset: Assets.svg.copy,
- context: context,
- );
- },
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- "Order ID",
- style: isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(context)
- : STextStyles.itemSubtitle12(context),
- ),
- Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- SelectableText(
- order.orderId,
- style: isDesktop
- ? STextStyles.desktopTextSmall(context)
- : STextStyles.titleBold12(context),
- ),
- const SizedBox(width: 6),
- Icon(
- Icons.copy,
- size: 14,
- color: Theme.of(
- context,
- ).extension<StackColors>()!.accentColorBlue,
- ),
- ],
- ),
- ],
- ),
+ onPressed: () {
+ Clipboard.setData(ClipboardData(text: order.orderId));
+ showFloatingFlushBar(
+ type: FlushBarType.info,
+ message: "Order ID copied",
+ iconAsset: Assets.svg.copy,
+ context: context,
+ );
+ },
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "Order ID",
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.itemSubtitle12(context),
+ ),
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SelectableText(
+ order.orderId,
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.titleBold12(context),
+ ),
+ const SizedBox(width: 6),
+ IconCopyButton(data: order.orderId),
+ ],
+ ),
+ ],
),
),
SizedBox(height: isDesktop ? 16 : 12),
@@ -833,13 +809,7 @@ class _CakePayOrderViewState extends ConsumerState<CakePayOrderView> {
: STextStyles.itemSubtitle12(context),
),
const Spacer(),
- Icon(
- Icons.copy,
- size: 14,
- color: Theme.of(
- context,
- ).extension<StackColors>()!.accentColorBlue,
- ),
+ IconCopyButton(data: order.orderId),
const SizedBox(width: 4),
Text("Copy", style: STextStyles.link2(context)),
],
diff --git a/lib/pages/cakepay/cakepay_orders_view.dart b/lib/pages/cakepay/cakepay_orders_view.dart
index 48b9665..f808fcf 100644
--- a/lib/pages/cakepay/cakepay_orders_view.dart
+++ b/lib/pages/cakepay/cakepay_orders_view.dart
@@ -3,15 +3,15 @@ import 'package:flutter/material.dart';
import '../../services/cakepay/cakepay_service.dart';
import '../../services/cakepay/src/models/order.dart';
import '../../themes/stack_colors.dart';
+import '../../utilities/show_loading.dart';
import '../../utilities/text_styles.dart';
import '../../utilities/util.dart';
import '../../widgets/background.dart';
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 '../../widgets/dialogs/s_dialog.dart';
+import '../../widgets/rounded_container.dart';
import 'cakepay_order_view.dart';
class CakePayOrdersView extends StatefulWidget {
@@ -25,19 +25,23 @@ class CakePayOrdersView extends StatefulWidget {
class _CakePayOrdersViewState extends State<CakePayOrdersView> {
List<CakePayOrder> _orders = [];
- bool _syncing = false;
@override
void initState() {
super.initState();
- _syncFromApi();
+ WidgetsBinding.instance.addPostFrameCallback((_) => _syncFromApi());
}
- /// Fetch each locally-tracked order ID individually via getOrder()
- /// (which works with the seller API key, unlike getMyOrders()).
- /// Mirrors ShopInBit's _syncFromApi() pattern.
Future<void> _syncFromApi() async {
- setState(() => _syncing = true);
+ await showLoading(
+ context: context,
+ message: "Loading orders...",
+ whileFutureAlt: _syncFromApiHelper,
+ rootNavigator: Util.isDesktop,
+ );
+ }
+
+ Future<void> _syncFromApiHelper() async {
try {
final orderIds = await CakePayService.instance.getOrderIds();
final results = <CakePayOrder>[];
@@ -56,10 +60,6 @@ class _CakePayOrdersViewState extends State<CakePayOrdersView> {
}
} catch (_) {
// Fall back to empty list — no local cache to fall back on
- } finally {
- if (mounted) {
- setState(() => _syncing = false);
- }
}
}
@@ -67,162 +67,35 @@ class _CakePayOrdersViewState extends State<CakePayOrdersView> {
Widget build(BuildContext context) {
final isDesktop = Util.isDesktop;
- final list = _orders.isEmpty
- ? Center(
- child: Text(
- _syncing ? "Loading orders..." : "No orders yet",
- style: isDesktop
- ? STextStyles.desktopTextSmall(context)
- : STextStyles.itemSubtitle(context),
- ),
- )
- : ListView.separated(
- shrinkWrap: isDesktop,
- primary: isDesktop ? false : null,
- itemCount: _orders.length,
- separatorBuilder: (_, __) => SizedBox(height: isDesktop ? 16 : 12),
- itemBuilder: (context, index) {
- final order = _orders[index];
- return GestureDetector(
- onTap: () {
- if (isDesktop) {
- Navigator.of(context, rootNavigator: true).pop();
- showDialog<void>(
- context: context,
- builder: (_) => CakePayOrderView(orderId: order.orderId),
- );
- } else {
- Navigator.of(context).pushNamed(
- CakePayOrderView.routeName,
- arguments: order.orderId,
- );
- }
- },
- child: RoundedWhiteContainer(
- child: Row(
- children: [
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text(
- order.orderId.length > 8
- ? "${order.orderId.substring(0, 8)}..."
- : order.orderId,
- style: isDesktop
- ? STextStyles.desktopTextSmall(context)
- : STextStyles.titleBold12(context),
- ),
- 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>()!,
- ),
- ),
- ),
- ),
- ],
- ),
- if (order.amountUsd != null) ...[
- const SizedBox(height: 4),
- Text(
- "\$${order.amountUsd} USD",
- style: isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(
- context,
- )
- : STextStyles.itemSubtitle12(
- context,
- ).copyWith(
- color: Theme.of(context)
- .extension<StackColors>()!
- .textSubtitle1,
- ),
- ),
- ],
- ],
- ),
- ),
- SizedBox(width: isDesktop ? 16 : 8),
- Icon(
- Icons.chevron_right,
- color: Theme.of(
- context,
- ).extension<StackColors>()!.textSubtitle1,
- ),
- ],
- ),
- ),
- );
- },
- );
-
- final content = Stack(
- children: [
- list,
- if (_syncing) const LoadingIndicator(width: 24, height: 24),
- ],
- );
-
return ConditionalParent(
condition: isDesktop,
- builder: (child) => DesktopDialog(
- maxWidth: 580,
- maxHeight: 550,
- child: Column(
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Padding(
- padding: const EdgeInsets.only(left: 32),
- child: Text(
- "My Orders",
- style: STextStyles.desktopH3(context),
+ builder: (child) => SDialog(
+ child: SizedBox(
+ width: 580,
+ child: Column(
+ mainAxisSize: .min,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Padding(
+ padding: const EdgeInsets.only(left: 32),
+ child: Text(
+ "My Orders",
+ style: STextStyles.desktopH3(context),
+ ),
),
+ const DesktopDialogCloseButton(),
+ ],
+ ),
+ Flexible(
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 32),
+ child: child,
),
- const DesktopDialogCloseButton(),
- ],
- ),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 32,
- vertical: 16,
- ),
- child: child,
),
- ),
- ],
+ ],
+ ),
),
),
child: ConditionalParent(
@@ -243,7 +116,123 @@ class _CakePayOrdersViewState extends State<CakePayOrdersView> {
),
),
),
- child: content,
+ child: _orders.isEmpty
+ ? Center(
+ child: Text(
+ "No orders yet",
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.itemSubtitle(context),
+ ),
+ )
+ : ListView.separated(
+ shrinkWrap: isDesktop,
+ primary: isDesktop ? false : null,
+ itemCount: _orders.length,
+ padding: isDesktop ? const .only(bottom: 32, top: 16) : null,
+ separatorBuilder: (_, __) =>
+ SizedBox(height: isDesktop ? 16 : 12),
+ itemBuilder: (context, index) {
+ final order = _orders[index];
+ return RoundedContainer(
+ padding: .all(Util.isDesktop ? 16 : 12),
+ borderColor: Util.isDesktop
+ ? Theme.of(
+ context,
+ ).extension<StackColors>()!.textFieldDefaultBG
+ : null,
+ color: Theme.of(context).extension<StackColors>()!.popupBG,
+ onPressed: () {
+ Navigator.of(
+ context,
+ ).pushNamed(CakePayOrderView.routeName, arguments: order);
+ },
+ child: Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ mainAxisAlignment:
+ MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ order.orderId.length > 8
+ ? "${order.orderId.substring(0, 8)}..."
+ : order.orderId,
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.titleBold12(context),
+ ),
+ 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>()!,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ if (order.amountUsd != null) ...[
+ const SizedBox(height: 4),
+ Text(
+ "\$${order.amountUsd} USD",
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(
+ context,
+ )
+ : STextStyles.itemSubtitle12(
+ context,
+ ).copyWith(
+ color: Theme.of(context)
+ .extension<StackColors>()!
+ .textSubtitle1,
+ ),
+ ),
+ ],
+ ],
+ ),
+ ),
+ SizedBox(width: isDesktop ? 16 : 8),
+ Icon(
+ Icons.chevron_right,
+ color: Theme.of(
+ context,
+ ).extension<StackColors>()!.textSubtitle1,
+ ),
+ ],
+ ),
+ );
+ },
+ ),
),
);
}
diff --git a/lib/pages/cakepay/cakepay_vendors_view.dart b/lib/pages/cakepay/cakepay_vendors_view.dart
index 5c16cdd..d04f05b 100644
--- a/lib/pages/cakepay/cakepay_vendors_view.dart
+++ b/lib/pages/cakepay/cakepay_vendors_view.dart
@@ -17,7 +17,7 @@ import '../../widgets/desktop/desktop_dialog.dart';
import '../../widgets/desktop/desktop_dialog_close_button.dart';
import '../../widgets/icon_widgets/credit_card_icon.dart';
import '../../widgets/loading_indicator.dart';
-import '../../widgets/rounded_white_container.dart';
+import '../../widgets/rounded_container.dart';
import '../../widgets/stack_text_field.dart';
import 'cakepay_card_detail_view.dart';
@@ -98,21 +98,9 @@ class _CakePayVendorsViewState extends State<CakePayVendorsView> {
}
Future<void> _onCardTapped(CakePayCard card) async {
- if (Util.isDesktop) {
- // this pop makes going back annoying as the whole list needs to be
- // searched again with API calls etc. Leaving in for now as this is how I
- // found it and removing here could introduce worse issues somewhere else.
- Navigator.of(context, rootNavigator: true).pop();
-
- await showDialog<void>(
- context: context,
- builder: (_) => CakePayCardDetailView(card: card),
- );
- } else {
- await Navigator.of(
- context,
- ).pushNamed(CakePayCardDetailView.routeName, arguments: card);
- }
+ await Navigator.of(
+ context,
+ ).pushNamed(CakePayCardDetailView.routeName, arguments: card);
}
@override
@@ -143,10 +131,7 @@ class _CakePayVendorsViewState extends State<CakePayVendorsView> {
),
Flexible(
child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 32,
- vertical: 8,
- ),
+ padding: const .only(left: 32, right: 32, top: 8),
child: child,
),
),
@@ -213,9 +198,7 @@ class _CakePayVendorsViewState extends State<CakePayVendorsView> {
shrinkWrap: isDesktop,
primary: isDesktop ? false : null,
itemCount: cards.length,
- padding: isDesktop
- ? null
- : const EdgeInsets.only(bottom: 16),
+ padding: .only(bottom: isDesktop ? 32 : 16),
separatorBuilder: (_, __) =>
SizedBox(height: isDesktop ? 16 : 12),
itemBuilder: (_, index) => _CardTile(
@@ -416,66 +399,67 @@ class _CardTile extends StatelessWidget {
final isDesktop = Util.isDesktop;
final colors = Theme.of(context).extension<StackColors>()!;
- return GestureDetector(
- onTap: onTap,
- child: RoundedWhiteContainer(
- child: Row(
- children: [
- ClipRRect(
- borderRadius: BorderRadius.circular(4),
- child: card.cardImageUrl != null
- ? Image.network(
- card.cardImageUrl!,
- width: isDesktop ? 60 : 48,
- height: isDesktop ? 40 : 32,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => CreditCardIcon(
- width: isDesktop ? 40 : 32,
- height: isDesktop ? 40 : 32,
- ),
- )
- : CreditCardIcon(
+ return RoundedContainer(
+ color: colors.popupBG,
+ borderColor: isDesktop ? colors.textFieldDefaultBG : null,
+ onPressed: onTap,
+ padding: isDesktop ? const .all(16) : const .all(12),
+ child: Row(
+ children: [
+ ClipRRect(
+ borderRadius: BorderRadius.circular(4),
+ child: card.cardImageUrl != null
+ ? Image.network(
+ card.cardImageUrl!,
+ width: isDesktop ? 60 : 48,
+ height: isDesktop ? 40 : 32,
+ fit: BoxFit.cover,
+ errorBuilder: (_, __, ___) => CreditCardIcon(
width: isDesktop ? 40 : 32,
height: isDesktop ? 40 : 32,
),
- ),
- const SizedBox(width: 12),
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- card.name,
- style: isDesktop
- ? STextStyles.desktopTextSmall(context)
- : STextStyles.titleBold12(context),
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
+ )
+ : CreditCardIcon(
+ width: isDesktop ? 40 : 32,
+ height: isDesktop ? 40 : 32,
),
- const SizedBox(height: 2),
- Text(
- [
- if (card.denominationRange.isNotEmpty)
- card.denominationRange,
- if (card.currencyCode != null) card.currencyCode!,
- ].join(' '),
- style: isDesktop
- ? STextStyles.desktopTextExtraExtraSmall(context)
- : STextStyles.itemSubtitle12(
- context,
- ).copyWith(color: colors.textSubtitle1),
- ),
- ],
- ),
- ),
- SvgPicture.asset(
- Assets.svg.chevronRight,
- width: 20,
- height: 20,
- colorFilter: ColorFilter.mode(colors.textSubtitle1, .srcIn),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ card.name,
+ style: isDesktop
+ ? STextStyles.desktopTextSmall(context)
+ : STextStyles.titleBold12(context),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ),
+ const SizedBox(height: 2),
+ Text(
+ [
+ if (card.denominationRange.isNotEmpty)
+ card.denominationRange,
+ if (card.currencyCode != null) card.currencyCode!,
+ ].join(' '),
+ style: isDesktop
+ ? STextStyles.desktopTextExtraExtraSmall(context)
+ : STextStyles.itemSubtitle12(
+ context,
+ ).copyWith(color: colors.textSubtitle1),
+ ),
+ ],
),
- ],
- ),
+ ),
+ SvgPicture.asset(
+ Assets.svg.chevronRight,
+ width: 20,
+ height: 20,
+ colorFilter: ColorFilter.mode(colors.textSubtitle1, .srcIn),
+ ),
+ ],
),
);
}
diff --git a/lib/pages/shopinbit/shopinbit_tickets_view.dart b/lib/pages/shopinbit/shopinbit_tickets_view.dart
index 560af5e..c71782a 100644
--- a/lib/pages/shopinbit/shopinbit_tickets_view.dart
+++ b/lib/pages/shopinbit/shopinbit_tickets_view.dart
@@ -380,7 +380,8 @@ class _RequestRow extends StatelessWidget {
SizedBox(width: isDesktop ? 16 : 8),
SvgPicture.asset(
Assets.svg.chevronRight,
- width: 14,
+ width: 20,
+ height: 20,
colorFilter: ColorFilter.mode(stackColors.textSubtitle1, .srcIn),
),
],
diff --git a/lib/pages_desktop_specific/services/cakepay/desktop_gift_cards_view.dart b/lib/pages_desktop_specific/services/cakepay/desktop_gift_cards_view.dart
index 964028a..22b077a 100644
--- a/lib/pages_desktop_specific/services/cakepay/desktop_gift_cards_view.dart
+++ b/lib/pages_desktop_specific/services/cakepay/desktop_gift_cards_view.dart
@@ -10,6 +10,7 @@ import '../../../themes/stack_colors.dart';
import '../../../utilities/text_styles.dart';
import '../../../widgets/desktop/primary_button.dart';
import '../../../widgets/desktop/secondary_button.dart';
+import '../../../widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog.dart';
import '../../../widgets/icon_widgets/credit_card_icon.dart';
import '../../../widgets/rounded_white_container.dart';
import '../../../widgets/tor_subscription.dart';
@@ -105,7 +106,9 @@ class _DesktopGiftCardsViewState extends ConsumerState<DesktopGiftCardsView> {
onPressed: () {
showDialog<void>(
context: context,
- builder: (_) => const CakePayVendorsView(),
+ builder: (_) => const NestedNavigatorDialog(
+ initialRoute: CakePayVendorsView.routeName,
+ ),
);
},
),
@@ -118,7 +121,9 @@ class _DesktopGiftCardsViewState extends ConsumerState<DesktopGiftCardsView> {
onPressed: () {
showDialog<void>(
context: context,
- builder: (_) => const CakePayOrdersView(),
+ builder: (_) => const NestedNavigatorDialog(
+ initialRoute: CakePayOrdersView.routeName,
+ ),
);
},
),
diff --git a/lib/route_generator.dart b/lib/route_generator.dart
index 5aa2396..2a42a8a 100644
--- a/lib/route_generator.dart
+++ b/lib/route_generator.dart
@@ -258,6 +258,7 @@ import 'pages_desktop_specific/settings/settings_menu/syncing_preferences_settin
import 'pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart';
import 'pages_desktop_specific/spark_coins/spark_coins_view.dart';
import 'services/cakepay/src/models/card.dart';
+import 'services/cakepay/src/models/order.dart';
import 'services/event_bus/events/global/node_connection_status_changed_event.dart';
import 'services/event_bus/events/global/wallet_sync_status_changed_event.dart';
import 'services/shopinbit/src/models/car_research.dart';
@@ -1105,10 +1106,10 @@ class RouteGenerator {
return _routeError("${settings.name} invalid args: ${args.toString()}");
case CakePayOrderView.routeName:
- if (args is String) {
+ if (args is CakePayOrder) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
- builder: (_) => CakePayOrderView(orderId: args),
+ builder: (_) => CakePayOrderView(order: args),
settings: RouteSettings(name: settings.name),
);
}
diff --git a/lib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dart b/lib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dart
index a5ce6ab..3aa7d81 100644
--- a/lib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dart
+++ b/lib/widgets/dialogs/nested_navigator_dialog/nested_navigator_dialog_route_generator.dart
@@ -3,6 +3,10 @@ import 'dart:math';
import 'package:flutter/material.dart';
import '../../../models/shopinbit/shopinbit_order_model.dart';
+import '../../../pages/cakepay/cakepay_card_detail_view.dart';
+import '../../../pages/cakepay/cakepay_order_view.dart';
+import '../../../pages/cakepay/cakepay_orders_view.dart';
+import '../../../pages/cakepay/cakepay_vendors_view.dart';
import '../../../pages/shopinbit/shopinbit_step_1.dart';
import '../../../pages/shopinbit/shopinbit_step_2.dart';
import '../../../pages/shopinbit/shopinbit_step_3.dart';
@@ -10,6 +14,8 @@ import '../../../pages/shopinbit/shopinbit_step_4.dart';
import '../../../pages/shopinbit/shopinbit_ticket_detail.dart';
import '../../../pages/shopinbit/shopinbit_tickets_view.dart';
import '../../../pages_desktop_specific/services/shopin_bit/sub_widgets/desktop_shopin_bit_first_run.dart';
+import '../../../services/cakepay/src/models/card.dart';
+import '../../../services/cakepay/src/models/order.dart';
import '../../../utilities/text_styles.dart';
import '../../../utilities/util.dart';
import '../../conditional_parent.dart';
@@ -105,6 +111,44 @@ abstract final class NestedNavigatorDialogRouteGenerator {
"Expected ShopInBitOrderModel",
);
+ case CakePayVendorsView.routeName:
+ return getRoute(
+ builder: (_) => const CakePayVendorsView(),
+ settings: RouteSettings(name: settings.name),
+ );
+
+ case CakePayOrdersView.routeName:
+ return getRoute(
+ builder: (_) => const CakePayOrdersView(),
+ settings: RouteSettings(name: settings.name),
+ );
+
+ case CakePayCardDetailView.routeName:
+ if (args is CakePayCard) {
+ return getRoute(
+ builder: (_) => CakePayCardDetailView(card: args),
+ settings: RouteSettings(name: settings.name),
+ );
+ }
+ return _routeError(
+ "${settings.name} invalid args\n"
+ "Got ${args.runtimeType}\n"
+ "Expected CakePayCard",
+ );
+
+ case CakePayOrderView.routeName:
+ if (args is CakePayOrder) {
+ return getRoute(
+ builder: (_) => CakePayOrderView(order: args),
+ settings: RouteSettings(name: settings.name),
+ );
+ }
+ return _routeError(
+ "${settings.name} invalid args\n"
+ "Got ${args.runtimeType}\n"
+ "Expected CakePayOrder",
+ );
+
default:
return _routeError("Unknown route name: ${settings.name}");
}
Why this scored 18/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.