Merge branch 'refs/heads/ui-enhancements2' into ui-enhancements

This commit is contained in:
tuxpizza 2024-12-21 23:18:00 -05:00
commit 03ab60b96a
4 changed files with 490 additions and 124 deletions

View file

@ -1,4 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'dart:ui';
import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart'; import 'package:cake_wallet/core/wallet_connect/wc_bottom_sheet_service.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
@ -37,6 +38,8 @@ import 'package:cake_wallet/src/screens/release_notes/release_notes_screen.dart'
import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart';
import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; import 'package:cake_wallet/themes/extensions/balance_page_theme.dart';
import '../../../themes/theme_base.dart';
class DashboardPage extends StatefulWidget { class DashboardPage extends StatefulWidget {
DashboardPage({ DashboardPage({
required this.bottomSheetService, required this.bottomSheetService,
@ -59,13 +62,14 @@ class _DashboardPageState extends State<DashboardPage> {
void initState() { void initState() {
super.initState(); super.initState();
bool isMobileLayout = bool isMobileLayout = responsiveLayoutUtil.screenWidth <
responsiveLayoutUtil.screenWidth < ResponsiveLayoutUtilBase.kMobileThreshold; ResponsiveLayoutUtilBase.kMobileThreshold;
reaction((_) => responsiveLayoutUtil.screenWidth, (screenWidth) { reaction((_) => responsiveLayoutUtil.screenWidth, (screenWidth) {
// Check if it was previously in mobile layout, and now changing to desktop // Check if it was previously in mobile layout, and now changing to desktop
if (isMobileLayout && if (isMobileLayout &&
screenWidth > ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) { screenWidth >
ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) {
setState(() { setState(() {
isMobileLayout = false; isMobileLayout = false;
}); });
@ -73,7 +77,8 @@ class _DashboardPageState extends State<DashboardPage> {
// Check if it was previously in desktop layout, and now changing to mobile // Check if it was previously in desktop layout, and now changing to mobile
if (!isMobileLayout && if (!isMobileLayout &&
screenWidth <= ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) { screenWidth <=
ResponsiveLayoutUtilBase.kDesktopMaxDashBoardWidthConstraint) {
setState(() { setState(() {
isMobileLayout = true; isMobileLayout = true;
}); });
@ -134,13 +139,15 @@ class _DashboardPageView extends BasePage {
@override @override
Widget Function(BuildContext, Widget) get rootWrapper => Widget Function(BuildContext, Widget) get rootWrapper =>
(BuildContext context, Widget scaffold) => GradientBackground(scaffold: scaffold); (BuildContext context, Widget scaffold) =>
GradientBackground(scaffold: scaffold);
@override @override
bool get resizeToAvoidBottomInset => false; bool get resizeToAvoidBottomInset => false;
@override @override
Widget get endDrawer => MenuWidget(dashboardViewModel, ValueKey('dashboard_page_drawer_menu_widget_key')); Widget get endDrawer => MenuWidget(
dashboardViewModel, ValueKey('dashboard_page_drawer_menu_widget_key'));
@override @override
Widget leading(BuildContext context) { Widget leading(BuildContext context) {
@ -160,7 +167,8 @@ class _DashboardPageView extends BasePage {
return SyncIndicator( return SyncIndicator(
key: ValueKey('dashboard_page_sync_indicator_button_key'), key: ValueKey('dashboard_page_sync_indicator_button_key'),
dashboardViewModel: dashboardViewModel, dashboardViewModel: dashboardViewModel,
onTap: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync), onTap: () => Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.connectionSync),
); );
} }
@ -168,7 +176,8 @@ class _DashboardPageView extends BasePage {
Widget trailing(BuildContext context) { Widget trailing(BuildContext context) {
final menuButton = Image.asset( final menuButton = Image.asset(
'assets/images/menu.png', 'assets/images/menu.png',
color: Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor, color:
Theme.of(context).extension<DashboardPageTheme>()!.pageTitleTextColor,
); );
return Container( return Container(
@ -190,7 +199,8 @@ class _DashboardPageView extends BasePage {
final BottomSheetService bottomSheetService; final BottomSheetService bottomSheetService;
final WalletAddressListViewModel addressListViewModel; final WalletAddressListViewModel addressListViewModel;
int get initialPage => dashboardViewModel.shouldShowMarketPlaceInDashboard ? 1 : 0; int get initialPage =>
dashboardViewModel.shouldShowMarketPlaceInDashboard ? 1 : 0;
ObservableList<Widget> pages = ObservableList<Widget>(); ObservableList<Widget> pages = ObservableList<Widget>();
bool _isEffectsInstalled = false; bool _isEffectsInstalled = false;
StreamSubscription<bool>? _onInactiveSub; StreamSubscription<bool>? _onInactiveSub;
@ -219,14 +229,18 @@ class _DashboardPageView extends BasePage {
_setEffects(context); _setEffects(context);
return SafeArea( return SafeArea(
minimum: EdgeInsets.only(bottom: 24), minimum: EdgeInsets.only(bottom: 0),
child: BottomSheetListener( child: BottomSheetListener(
bottomSheetService: bottomSheetService, bottomSheetService: bottomSheetService,
child: Column( child: Container(
mainAxisSize: MainAxisSize.max, child: Stack(
//fit: StackFit.expand,
alignment: Alignment.bottomCenter,
//mainAxisSize: MainAxisSize.max,
//alignment: Alignment.bottomCenter,
children: <Widget>[ children: <Widget>[
Expanded( //new Expanded(
child: Observer( Observer(
builder: (context) { builder: (context) {
return PageView.builder( return PageView.builder(
key: ValueKey('dashboard_page_view_key'), key: ValueKey('dashboard_page_view_key'),
@ -236,9 +250,25 @@ class _DashboardPageView extends BasePage {
); );
}, },
), ),
), //),
Padding( Positioned(
padding: EdgeInsets.only(bottom: 24, top: 10), top: 590,
child: Container(
// decoration: BoxDecoration(
// boxShadow: [
// BoxShadow(
// color: Theme.of(context)
// .extension<DashboardPageTheme>()!
// .thirdGradientBackgroundColor
// .withAlpha(50),
// spreadRadius: 5,
// blurRadius: 7,
// offset: Offset(0, 3),
// )
// ]
// ),
alignment: Alignment.bottomCenter,
//padding: EdgeInsets.only(top: 50),
child: Observer( child: Observer(
builder: (context) { builder: (context) {
return Semantics( return Semantics(
@ -268,33 +298,95 @@ class _DashboardPageView extends BasePage {
}, },
), ),
), ),
Observer( ),
currentTheme.type == ThemeType.bright
? Positioned(
top: 560,
bottom: 0,
left: 0,
right: 0,
child: Observer(
builder: (_) { builder: (_) {
return ClipRect( return ClipRect(
child: Container( child: Container(
margin: const EdgeInsets.only(left: 16, right: 16), decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor
.withAlpha(10),
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor
.withAlpha(75),
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor
.withAlpha(150),
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor,
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor
// Color.fromARGB(10, 245, 8, 82),
// Color.fromARGB(75, 245, 8, 82),
// Color.fromARGB(150, 245, 8, 82),
// Color.fromARGB(200, 245, 8, 82),
// Color.fromARGB(255, 245, 8, 82),
],
),
),
child: Container( child: Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.only(
left: 16, right: 16, bottom: 24, top: 48),
child: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
child: Container(
//clipBehavior: Clip.hardEdge,
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0), borderRadius: BorderRadius.circular(50.0),
border: Border.all( border: Border.all(
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor, color: Theme.of(context)
.extension<BalancePageTheme>()!
.cardBorderColor,
width: 1, width: 1,
), ),
color: Theme.of(context) color: Theme.of(context)
.extension<SyncIndicatorTheme>()! .extension<SyncIndicatorTheme>()!
.syncedBackgroundColor, .syncedBackgroundColor,
boxShadow: [
BoxShadow(
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor
.withAlpha(50),
spreadRadius: 5,
blurRadius: 7
)
],
), ),
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 10), padding: EdgeInsets.symmetric(horizontal: 10),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: MainActions.all children: MainActions.all
.where((element) => element.canShow?.call(dashboardViewModel) ?? true) .where((element) =>
element.canShow
?.call(dashboardViewModel) ??
true)
.map( .map(
(action) => Expanded( (action) => Expanded(
child: Semantics( child: Semantics(
button: true, button: true,
enabled: (action.isEnabled?.call(dashboardViewModel) ?? true), enabled: (action.isEnabled?.call(
dashboardViewModel) ??
true),
child: ActionButton( child: ActionButton(
key: ValueKey( key: ValueKey(
'dashboard_page_${action.name(context)}_action_button_key'), 'dashboard_page_${action.name(context)}_action_button_key'),
@ -302,21 +394,29 @@ class _DashboardPageView extends BasePage {
action.image, action.image,
height: 24, height: 24,
width: 24, width: 24,
color: action.isEnabled?.call(dashboardViewModel) ?? true color: action.isEnabled?.call(
dashboardViewModel) ??
true
? Theme.of(context) ? Theme.of(context)
.extension<DashboardPageTheme>()! .extension<
DashboardPageTheme>()!
.mainActionsIconColor .mainActionsIconColor
: Theme.of(context) : Theme.of(context)
.extension<BalancePageTheme>()! .extension<
BalancePageTheme>()!
.labelTextColor, .labelTextColor,
), ),
title: action.name(context), title: action.name(context),
onClick: () async => onClick: () async =>
await action.onTap(context, dashboardViewModel), await action.onTap(context,
textColor: action.isEnabled?.call(dashboardViewModel) ?? true dashboardViewModel),
textColor: action.isEnabled?.call(
dashboardViewModel) ??
true
? null ? null
: Theme.of(context) : Theme.of(context)
.extension<BalancePageTheme>()! .extension<
BalancePageTheme>()!
.labelTextColor, .labelTextColor,
), ),
), ),
@ -327,12 +427,147 @@ class _DashboardPageView extends BasePage {
), ),
), ),
), ),
// ],
),
),
),
); );
}, },
), ),
)
:
Positioned(
top: 560,
bottom: 0,
left: 0,
right: 0,
child: Observer(
builder: (_) {
return ClipRect(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor
.withAlpha(10),
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor
.withAlpha(75),
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor
.withAlpha(150),
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor,
Theme.of(context)
.extension<DashboardPageTheme>()!
.thirdGradientBackgroundColor
// Color.fromARGB(10, 245, 8, 82),
// Color.fromARGB(75, 245, 8, 82),
// Color.fromARGB(150, 245, 8, 82),
// Color.fromARGB(200, 245, 8, 82),
// Color.fromARGB(255, 245, 8, 82),
], ],
), ),
), ),
child: Container(
alignment: Alignment.bottomCenter,
padding: const EdgeInsets.only(
left: 16, right: 16, bottom: 24, top: 48),
child: Container(
//clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
border: Border.all(
color: Theme.of(context)
.extension<BalancePageTheme>()!
.cardBorderColor,
width: 1,
),
color: Theme.of(context)
.extension<SyncIndicatorTheme>()!
.syncedBackgroundColor,
boxShadow: [
BoxShadow(
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor
.withAlpha(50),
spreadRadius: 5,
blurRadius: 7
)
],
),
child: Container(
padding: EdgeInsets.symmetric(horizontal: 10),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: MainActions.all
.where((element) =>
element.canShow
?.call(dashboardViewModel) ??
true)
.map(
(action) => Expanded(
child: Semantics(
button: true,
enabled: (action.isEnabled?.call(
dashboardViewModel) ??
true),
child: ActionButton(
key: ValueKey(
'dashboard_page_${action.name(context)}_action_button_key'),
image: Image.asset(
action.image,
height: 24,
width: 24,
color: action.isEnabled?.call(
dashboardViewModel) ??
true
? Theme.of(context)
.extension<
DashboardPageTheme>()!
.mainActionsIconColor
: Theme.of(context)
.extension<
BalancePageTheme>()!
.labelTextColor,
),
title: action.name(context),
onClick: () async =>
await action.onTap(context,
dashboardViewModel),
textColor: action.isEnabled?.call(
dashboardViewModel) ??
true
? null
: Theme.of(context)
.extension<
BalancePageTheme>()!
.labelTextColor,
),
),
),
)
.toList(),
),
),
),
),
),
);
},
),
),
],
),
),
),
); );
} }
@ -400,9 +635,10 @@ class _DashboardPageView extends BasePage {
void _showReleaseNotesPopup(BuildContext context) async { void _showReleaseNotesPopup(BuildContext context) async {
final sharedPrefs = await SharedPreferences.getInstance(); final sharedPrefs = await SharedPreferences.getInstance();
final currentAppVersion = final currentAppVersion = VersionComparator.getExtendedVersionNumber(
VersionComparator.getExtendedVersionNumber(dashboardViewModel.settingsStore.appVersion); dashboardViewModel.settingsStore.appVersion);
final lastSeenAppVersion = sharedPrefs.getInt(PreferencesKey.lastSeenAppVersion); final lastSeenAppVersion =
sharedPrefs.getInt(PreferencesKey.lastSeenAppVersion);
final isNewInstall = sharedPrefs.getBool(PreferencesKey.isNewInstall); final isNewInstall = sharedPrefs.getBool(PreferencesKey.isNewInstall);
if (currentAppVersion != lastSeenAppVersion && !isNewInstall!) { if (currentAppVersion != lastSeenAppVersion && !isNewInstall!) {
@ -427,7 +663,8 @@ class _DashboardPageView extends BasePage {
} }
void _showVulnerableSeedsPopup(BuildContext context) async { void _showVulnerableSeedsPopup(BuildContext context) async {
final List<String> affectedWalletNames = await dashboardViewModel.checkAffectedWallets(); final List<String> affectedWalletNames =
await dashboardViewModel.checkAffectedWallets();
if (affectedWalletNames.isNotEmpty) { if (affectedWalletNames.isNotEmpty) {
Future<void>.delayed( Future<void>.delayed(
@ -445,7 +682,8 @@ class _DashboardPageView extends BasePage {
} }
void _showHavenPopup(BuildContext context) async { void _showHavenPopup(BuildContext context) async {
final List<String> havenWalletList = await dashboardViewModel.checkForHavenWallets(); final List<String> havenWalletList =
await dashboardViewModel.checkForHavenWallets();
if (havenWalletList.isNotEmpty) { if (havenWalletList.isNotEmpty) {
Future<void>.delayed( Future<void>.delayed(

View file

@ -304,7 +304,7 @@ class CryptoBalanceWidget extends StatelessWidget {
)) ))
], ],
if (dashboardViewModel.showSilentPaymentsCard) ...[ if (dashboardViewModel.showSilentPaymentsCard) ...[
SizedBox(height: 10), SizedBox(height: 15),
Padding( Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: DashBoardRoundedCardWidget( child: DashBoardRoundedCardWidget(
@ -579,6 +579,14 @@ class BalanceRowWidget extends StatelessWidget {
Container( Container(
margin: const EdgeInsets.only(left: 16, right: 16), margin: const EdgeInsets.only(left: 16, right: 16),
decoration: BoxDecoration( decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor
.withAlpha(50),
spreadRadius: 3,
blurRadius: 7
)
],
borderRadius: BorderRadius.circular(30.0), borderRadius: BorderRadius.circular(30.0),
border: Border.all( border: Border.all(
color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor, color: Theme.of(context).extension<BalancePageTheme>()!.cardBorderColor,

View file

@ -23,13 +23,11 @@ class CakeFeaturesPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0), padding: EdgeInsets.symmetric(horizontal: 20),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox(height: 50), SizedBox(height: 10),
Text( Text(
'Cake ${S.of(context).features}', 'Cake ${S.of(context).features}',
style: TextStyle( style: TextStyle(
@ -60,6 +58,96 @@ class CakeFeaturesPage extends StatelessWidget {
), ),
), ),
SizedBox(height: 10), SizedBox(height: 10),
DashBoardRoundedCardWidget(
onTap: () {
if (Platform.isMacOS) {
_launchUrl("buy.cakepay.com");
} else {
_navigatorToGiftCardsPage(context);
}
},
title: 'Cake Pay',
subTitle: S.of(context).cake_pay_subtitle,
image: Image.asset(
'assets/images/cards.png',
height: 100,
width: 115,
fit: BoxFit.cover,
),
),
SizedBox(height: 10),
DashBoardRoundedCardWidget(
onTap: () {
if (Platform.isMacOS) {
_launchUrl("buy.cakepay.com");
} else {
_navigatorToGiftCardsPage(context);
}
},
title: 'Cake Pay',
subTitle: S.of(context).cake_pay_subtitle,
image: Image.asset(
'assets/images/cards.png',
height: 100,
width: 115,
fit: BoxFit.cover,
),
),
SizedBox(height: 10),
DashBoardRoundedCardWidget(
onTap: () {
if (Platform.isMacOS) {
_launchUrl("buy.cakepay.com");
} else {
_navigatorToGiftCardsPage(context);
}
},
title: 'Cake Pay',
subTitle: S.of(context).cake_pay_subtitle,
image: Image.asset(
'assets/images/cards.png',
height: 100,
width: 115,
fit: BoxFit.cover,
),
),
SizedBox(height: 10),
DashBoardRoundedCardWidget(
onTap: () {
if (Platform.isMacOS) {
_launchUrl("buy.cakepay.com");
} else {
_navigatorToGiftCardsPage(context);
}
},
title: 'Cake Pay',
subTitle: S.of(context).cake_pay_subtitle,
image: Image.asset(
'assets/images/cards.png',
height: 100,
width: 115,
fit: BoxFit.cover,
),
),
SizedBox(height: 10),
DashBoardRoundedCardWidget(
onTap: () {
if (Platform.isMacOS) {
_launchUrl("buy.cakepay.com");
} else {
_navigatorToGiftCardsPage(context);
}
},
title: 'Cake Pay',
subTitle: S.of(context).cake_pay_subtitle,
image: Image.asset(
'assets/images/cards.png',
height: 100,
width: 115,
fit: BoxFit.cover,
),
),
SizedBox(height: 10),
DashBoardRoundedCardWidget( DashBoardRoundedCardWidget(
onTap: () => _launchUrl("cake.nano-gpt.com"), onTap: () => _launchUrl("cake.nano-gpt.com"),
title: "NanoGPT", title: "NanoGPT",
@ -90,12 +178,12 @@ class CakeFeaturesPage extends StatelessWidget {
); );
}, },
), ),
SizedBox(height: 125),
], ],
), ),
), ),
], ],
), ),
),
); );
} }

View file

@ -32,6 +32,8 @@ import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import '../../../themes/extensions/dashboard_page_theme.dart';
class WalletListPage extends BasePage { class WalletListPage extends BasePage {
WalletListPage({ WalletListPage({
required this.walletListViewModel, required this.walletListViewModel,
@ -146,10 +148,11 @@ class WalletListBodyState extends State<WalletListBody> {
return Container( return Container(
padding: EdgeInsets.only(top: 16), padding: EdgeInsets.only(top: 16),
child: Column( child: Stack(
children: [ fit: StackFit.expand,
Expanded( alignment: Alignment.bottomCenter,
child: SingleChildScrollView( children: <Widget> [
SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -326,12 +329,39 @@ class WalletListBodyState extends State<WalletListBody> {
), ),
), ),
}, },
SizedBox(height: 150)
], ],
), ),
), ),
Positioned(
top: 505,
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: EdgeInsets.only(top: 50),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: <Color>[
Theme.of(context).colorScheme.background
.withAlpha(10),
Theme.of(context).colorScheme.background,
Theme.of(context).colorScheme.background,
Theme.of(context).colorScheme.background
// Color.fromARGB(10, 245, 8, 82),
// Color.fromARGB(75, 245, 8, 82),
// Color.fromARGB(150, 245, 8, 82),
// Color.fromARGB(200, 245, 8, 82),
// Color.fromARGB(255, 245, 8, 82),
],
), ),
Padding( ),
padding: const EdgeInsets.all(24), child: Container(
alignment: Alignment.bottomCenter,
//margin: EdgeInsets.only(top: 24),
padding: EdgeInsets.only(left: 24, right: 24),
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
PrimaryImageButton( PrimaryImageButton(
@ -396,6 +426,8 @@ class WalletListBodyState extends State<WalletListBody> {
], ],
), ),
), ),
),
),
], ],
), ),
); );