Skip to content

Commit 44d8794

Browse files
committed
Merge branch 'feat/cakepay' into vacay
2 parents 35ee6ab + b4196c4 commit 44d8794

26 files changed

Lines changed: 5117 additions & 106 deletions

lib/pages/cakepay/cakepay_card_detail_view.dart

Lines changed: 655 additions & 0 deletions
Large diffs are not rendered by default.

lib/pages/cakepay/cakepay_confirm_send_view.dart

Lines changed: 625 additions & 0 deletions
Large diffs are not rendered by default.

lib/pages/cakepay/cakepay_order_view.dart

Lines changed: 1052 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
import 'package:flutter/material.dart';
2+
3+
import '../../services/cakepay/cakepay_service.dart';
4+
import '../../services/cakepay/src/models/order.dart';
5+
import '../../themes/stack_colors.dart';
6+
import '../../utilities/text_styles.dart';
7+
import '../../utilities/util.dart';
8+
import '../../widgets/background.dart';
9+
import '../../widgets/conditional_parent.dart';
10+
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
11+
import '../../widgets/desktop/desktop_dialog.dart';
12+
import '../../widgets/desktop/desktop_dialog_close_button.dart';
13+
import '../../widgets/rounded_white_container.dart';
14+
import 'cakepay_order_view.dart';
15+
16+
class CakePayOrdersView extends StatefulWidget {
17+
const CakePayOrdersView({super.key});
18+
19+
static const String routeName = "/cakePayOrders";
20+
21+
@override
22+
State<CakePayOrdersView> createState() => _CakePayOrdersViewState();
23+
}
24+
25+
class _CakePayOrdersViewState extends State<CakePayOrdersView> {
26+
List<CakePayOrder> _orders = [];
27+
bool _syncing = false;
28+
29+
@override
30+
void initState() {
31+
super.initState();
32+
_syncFromApi();
33+
}
34+
35+
/// Fetch each locally-tracked order ID individually via getOrder()
36+
/// (which works with the seller API key, unlike getMyOrders()).
37+
/// Mirrors ShopInBit's _syncFromApi() pattern.
38+
Future<void> _syncFromApi() async {
39+
setState(() => _syncing = true);
40+
try {
41+
final orderIds = CakePayService.instance.getOrderIds();
42+
final results = <CakePayOrder>[];
43+
44+
for (final id in orderIds) {
45+
final resp = await CakePayService.instance.client.getOrder(id);
46+
if (!resp.hasError && resp.value != null) {
47+
var order = resp.value!;
48+
final override =
49+
CakePayService.devStatusOverrides[order.orderId];
50+
if (override != null) {
51+
order = order.copyWith(status: override);
52+
}
53+
results.add(order);
54+
}
55+
}
56+
57+
if (mounted) {
58+
setState(() {
59+
_orders = results;
60+
});
61+
}
62+
} catch (_) {
63+
// Fall back to empty list — no local cache to fall back on
64+
} finally {
65+
if (mounted) {
66+
setState(() => _syncing = false);
67+
}
68+
}
69+
}
70+
71+
String _statusLabel(CakePayOrderStatus status) {
72+
switch (status) {
73+
case CakePayOrderStatus.new_:
74+
return "New";
75+
case CakePayOrderStatus.expiredButStillPending:
76+
return "Expired (pending)";
77+
case CakePayOrderStatus.expired:
78+
return "Expired";
79+
case CakePayOrderStatus.failed:
80+
return "Failed";
81+
case CakePayOrderStatus.paid:
82+
return "Paid";
83+
case CakePayOrderStatus.paidPartial:
84+
return "Partially paid";
85+
case CakePayOrderStatus.pendingPurchase:
86+
return "Pending purchase";
87+
case CakePayOrderStatus.purchaseProcessing:
88+
return "Processing";
89+
case CakePayOrderStatus.purchased:
90+
return "Purchased";
91+
case CakePayOrderStatus.pendingEmail:
92+
return "Pending email";
93+
case CakePayOrderStatus.complete:
94+
return "Complete";
95+
case CakePayOrderStatus.pendingRefund:
96+
return "Pending refund";
97+
case CakePayOrderStatus.refunded:
98+
return "Refunded";
99+
}
100+
}
101+
102+
Color _statusColor(BuildContext context, CakePayOrderStatus status) {
103+
final colors = Theme.of(context).extension<StackColors>()!;
104+
switch (status) {
105+
case CakePayOrderStatus.complete:
106+
case CakePayOrderStatus.purchased:
107+
return colors.accentColorGreen;
108+
case CakePayOrderStatus.new_:
109+
case CakePayOrderStatus.paid:
110+
case CakePayOrderStatus.paidPartial:
111+
return colors.accentColorBlue;
112+
case CakePayOrderStatus.pendingPurchase:
113+
case CakePayOrderStatus.purchaseProcessing:
114+
case CakePayOrderStatus.pendingEmail:
115+
case CakePayOrderStatus.expiredButStillPending:
116+
return colors.accentColorYellow;
117+
case CakePayOrderStatus.expired:
118+
case CakePayOrderStatus.failed:
119+
case CakePayOrderStatus.pendingRefund:
120+
case CakePayOrderStatus.refunded:
121+
return colors.textSubtitle1;
122+
}
123+
}
124+
125+
@override
126+
Widget build(BuildContext context) {
127+
final isDesktop = Util.isDesktop;
128+
129+
final list = _orders.isEmpty
130+
? Center(
131+
child: Text(
132+
_syncing ? "Loading orders..." : "No orders yet",
133+
style: isDesktop
134+
? STextStyles.desktopTextSmall(context)
135+
: STextStyles.itemSubtitle(context),
136+
),
137+
)
138+
: ListView.separated(
139+
shrinkWrap: isDesktop,
140+
primary: isDesktop ? false : null,
141+
itemCount: _orders.length,
142+
separatorBuilder: (_, __) => SizedBox(height: isDesktop ? 16 : 12),
143+
itemBuilder: (context, index) {
144+
final order = _orders[index];
145+
return GestureDetector(
146+
onTap: () {
147+
if (isDesktop) {
148+
Navigator.of(context, rootNavigator: true).pop();
149+
showDialog<void>(
150+
context: context,
151+
builder: (_) => CakePayOrderView(orderId: order.orderId),
152+
);
153+
} else {
154+
Navigator.of(context).pushNamed(
155+
CakePayOrderView.routeName,
156+
arguments: order.orderId,
157+
);
158+
}
159+
},
160+
child: RoundedWhiteContainer(
161+
child: Row(
162+
children: [
163+
Expanded(
164+
child: Column(
165+
crossAxisAlignment: CrossAxisAlignment.start,
166+
children: [
167+
Row(
168+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
169+
children: [
170+
Text(
171+
order.orderId.length > 8
172+
? "${order.orderId.substring(0, 8)}..."
173+
: order.orderId,
174+
style: isDesktop
175+
? STextStyles.desktopTextSmall(context)
176+
: STextStyles.titleBold12(context),
177+
),
178+
Container(
179+
padding: const EdgeInsets.symmetric(
180+
horizontal: 8,
181+
vertical: 2,
182+
),
183+
decoration: BoxDecoration(
184+
borderRadius: BorderRadius.circular(8),
185+
color: _statusColor(
186+
context,
187+
order.status,
188+
).withValues(alpha: 0.2),
189+
),
190+
child: Text(
191+
_statusLabel(order.status),
192+
style:
193+
(isDesktop
194+
? STextStyles.desktopTextExtraExtraSmall(
195+
context,
196+
)
197+
: STextStyles.itemSubtitle12(
198+
context,
199+
))
200+
.copyWith(
201+
color: _statusColor(
202+
context,
203+
order.status,
204+
),
205+
),
206+
),
207+
),
208+
],
209+
),
210+
if (order.amountUsd != null) ...[
211+
const SizedBox(height: 4),
212+
Text(
213+
"\$${order.amountUsd} USD",
214+
style: isDesktop
215+
? STextStyles.desktopTextExtraExtraSmall(
216+
context,
217+
)
218+
: STextStyles.itemSubtitle12(
219+
context,
220+
).copyWith(
221+
color: Theme.of(context)
222+
.extension<StackColors>()!
223+
.textSubtitle1,
224+
),
225+
),
226+
],
227+
],
228+
),
229+
),
230+
SizedBox(width: isDesktop ? 16 : 8),
231+
Icon(
232+
Icons.chevron_right,
233+
color: Theme.of(
234+
context,
235+
).extension<StackColors>()!.textSubtitle1,
236+
),
237+
],
238+
),
239+
),
240+
);
241+
},
242+
);
243+
244+
final content = Stack(
245+
children: [
246+
list,
247+
if (_syncing)
248+
const Center(
249+
child: SizedBox(
250+
width: 24,
251+
height: 24,
252+
child: CircularProgressIndicator(strokeWidth: 2),
253+
),
254+
),
255+
],
256+
);
257+
258+
return ConditionalParent(
259+
condition: isDesktop,
260+
builder: (child) => DesktopDialog(
261+
maxWidth: 580,
262+
maxHeight: 550,
263+
child: Column(
264+
children: [
265+
Row(
266+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
267+
children: [
268+
Padding(
269+
padding: const EdgeInsets.only(left: 32),
270+
child: Text(
271+
"My Orders",
272+
style: STextStyles.desktopH3(context),
273+
),
274+
),
275+
const DesktopDialogCloseButton(),
276+
],
277+
),
278+
Expanded(
279+
child: Padding(
280+
padding: const EdgeInsets.symmetric(
281+
horizontal: 32,
282+
vertical: 16,
283+
),
284+
child: child,
285+
),
286+
),
287+
],
288+
),
289+
),
290+
child: ConditionalParent(
291+
condition: !isDesktop,
292+
builder: (child) => Background(
293+
child: Scaffold(
294+
backgroundColor: Theme.of(
295+
context,
296+
).extension<StackColors>()!.background,
297+
appBar: AppBar(
298+
leading: AppBarBackButton(
299+
onPressed: () => Navigator.of(context).pop(),
300+
),
301+
title: Text("My Orders", style: STextStyles.navBarTitle(context)),
302+
),
303+
body: SafeArea(
304+
child: Padding(padding: const EdgeInsets.all(16), child: child),
305+
),
306+
),
307+
),
308+
child: content,
309+
),
310+
);
311+
}
312+
}

0 commit comments

Comments
 (0)