implement user create card

This commit is contained in:
Godwin Asuquo 2022-06-07 06:14:35 +03:00
parent ac3b397908
commit 16616cf7d8
27 changed files with 790 additions and 182 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
assets/images/wifi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -7,14 +7,7 @@ import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/create_account_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/forgot_password_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/login_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/verify_otp_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'package:cake_wallet/src/screens/cake_pay/cards/buy_card_detail_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/cards/buy_gift_card.dart';
import 'package:cake_wallet/src/screens/cake_pay/cards/manage_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
import 'package:cw_core/unspent_coins_info.dart';
@ -652,8 +645,6 @@ Future setup(
getIt.registerFactory(() => AddressResolver(yatService: getIt.get<YatService>()));
getIt.registerFactory(() => ForgotPassword());
getIt.registerFactory(() => IoniaApi());
getIt.registerFactory<IoniaService>(
@ -661,17 +652,29 @@ Future setup(
getIt.registerFactory(() => IoniaViewModel(ioniaService: getIt.get<IoniaService>()));
getIt.registerFactory(() => CreateAccountPage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => IoniaCreateAccountPage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => LoginPage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => IoniaLoginPage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => VerifyIoniaOtp(getIt.get<IoniaViewModel>()));
getIt.registerFactoryParam<IoniaVerifyIoniaOtp, List, void>((List args, _) {
final email = args.first as String;
final ioniaViewModel = args[1] as IoniaViewModel;
getIt.registerFactory(() => WelcomePage(getIt.get<IoniaViewModel>()));
return IoniaVerifyIoniaOtp(ioniaViewModel, email);
});
getIt.registerFactory(() => BuyGiftCardPage());
getIt.registerFactory(() => IoniaWelcomePage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => IoniaBuyGiftCardPage());
getIt.registerFactory(() => IoniaBuyGiftCardDetailPage());
getIt.registerFactory(() => IoniaManageCardsPage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => IoniaDebitCardPage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => IoniaActivateDebitCardPage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => BuyGiftCardDetailPage());
_isSetupFinished = true;
}

View file

@ -88,7 +88,7 @@ class IoniaApi {
final isSuccessful = bodyJson['Successful'] as bool;
if (!isSuccessful) {
throw Exception(data['ErrorMessage'] as String);
throw Exception(data['message'] as String);
}
final virtualCard = data['VirtualCard'] as Map<String, Object>;
@ -117,7 +117,7 @@ class IoniaApi {
final isSuccessful = bodyJson['Successful'] as bool;
if (!isSuccessful) {
throw Exception(data['ErrorMessage'] as String);
throw Exception(data['message'] as String);
}
return IoniaVirtualCard.fromMap(data);

View file

@ -1,12 +1,13 @@
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
import 'package:flutter/material.dart';
abstract class IoniaCreateState {}
abstract class IoniaCreateAccountState {}
class IoniaCreateStateSuccess extends IoniaCreateState {}
class IoniaCreateStateSuccess extends IoniaCreateAccountState {}
class IoniaCreateStateLoading extends IoniaCreateState {}
class IoniaCreateStateLoading extends IoniaCreateAccountState {}
class IoniaCreateStateFailure extends IoniaCreateState {
class IoniaCreateStateFailure extends IoniaCreateAccountState {
IoniaCreateStateFailure({@required this.error});
final String error;
@ -27,3 +28,29 @@ class IoniaOtpFailure extends IoniaOtpState {
final String error;
}
class IoniaCreateCardState {}
class IoniaCreateCardSuccess extends IoniaCreateCardState {}
class IoniaCreateCardLoading extends IoniaCreateCardState {}
class IoniaCreateCardFailure extends IoniaCreateCardState {
IoniaCreateCardFailure({@required this.error});
final String error;
}
class IoniaFetchCardState {}
class IoniaNoCardState extends IoniaFetchCardState {}
class IoniaFetchingCard extends IoniaFetchCardState {}
class IoniaFetchCardFailure extends IoniaFetchCardState {}
class IoniaCardSuccess extends IoniaFetchCardState {
IoniaCardSuccess({@required this.card});
final IoniaVirtualCard card;
}

View file

@ -5,14 +5,6 @@ import 'package:cake_wallet/src/screens/backup/backup_page.dart';
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/create_account_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/forgot_password_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/login_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/auth/verify_otp_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart';
import 'package:cake_wallet/src/screens/cake_pay/cards/buy_card_detail_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/cards/buy_gift_card.dart';
import 'package:cake_wallet/src/screens/cake_pay/cards/manage_cards_page.dart';
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
@ -78,6 +70,7 @@ import 'package:hive/hive.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
RouteSettings currentRouteSettings;
@ -410,29 +403,33 @@ Route<dynamic> createRoute(RouteSettings settings) {
getIt.get<UnspentCoinsDetailsPage>(
param1: args));
case Routes.cakePayWelcomePage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<WelcomePage>());
case Routes.ioniaWelcomePage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaWelcomePage>());
case Routes.cakePayLoginPage:
return CupertinoPageRoute<void>( builder: (_) => getIt.get<LoginPage>());
case Routes.ioniaLoginPage:
return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaLoginPage>());
case Routes.cakePayCreateAccountPage:
return CupertinoPageRoute<void>( builder: (_) => getIt.get<CreateAccountPage>());
case Routes.ioniaCreateAccountPage:
return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaCreateAccountPage>());
case Routes.cakePayForgotPasswordPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ForgotPassword>());
case Routes.ioniaManageCardsPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaManageCardsPage>());
case Routes.ioniaBuyGiftCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaBuyGiftCardPage>());
case Routes.manageCardsPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<ManageCardsPage>());
case Routes.ioniaBuyGiftCardDetailPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaBuyGiftCardDetailPage>());
case Routes.buyGiftCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<BuyGiftCardPage>());
case Routes.buyGiftCardDetailPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<BuyGiftCardDetailPage>());
case Routes.ioniaVerifyIoniaOtpPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(builder: (_) =>getIt.get<IoniaVerifyIoniaOtp>(param1: args));
case Routes.verifyIoniaOtpPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<VerifyIoniaOtp>());
case Routes.ioniaDebitCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaDebitCardPage>());
case Routes.ioniaActivateDebitCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaActivateDebitCardPage>());
default:
return MaterialPageRoute<void>(

View file

@ -60,12 +60,14 @@ class Routes {
static const moneroRestoreWalletFromWelcome = '/monero_restore_wallet';
static const moneroNewWalletFromWelcome = '/monero_new_wallet';
static const addressPage = '/address_page';
static const cakePayWelcomePage = '/cake_pay_welcome_page';
static const cakePayCreateAccountPage = '/cake_pay_create_account_page';
static const cakePayLoginPage = '/cake_pay_login_page';
static const cakePayForgotPasswordPage = '/cake_pay_forgot_password_page';
static const manageCardsPage = '/manage_cards_page';
static const buyGiftCardPage = '/buy_gift_card_page';
static const buyGiftCardDetailPage = '/buy_gift_card_detail_page';
static const verifyIoniaOtpPage = '/cake_pay_verify_otp_page';
static const ioniaWelcomePage = '/cake_pay_welcome_page';
static const ioniaCreateAccountPage = '/cake_pay_create_account_page';
static const ioniaLoginPage = '/cake_pay_login_page';
static const ioniaManageCardsPage = '/manage_cards_page';
static const ioniaBuyGiftCardPage = '/buy_gift_card_page';
static const ioniaBuyGiftCardDetailPage = '/buy_gift_card_detail_page';
static const ioniaVerifyIoniaOtpPage = '/cake_pay_verify_otp_page';
static const ioniaDebitCardPage = '/debit_card_page';
static const ioniaActivateDebitCardPage = '/activate_debit_card_page';
}

View file

@ -1,55 +0,0 @@
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class ForgotPassword extends BasePage {
@override
Color get titleColor => Colors.black;
@override
Widget middle(BuildContext context) {
return Text(
S.current.forgot_password,
style: TextStyle(
fontSize: 22,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
),
);
}
@override
Widget body(BuildContext context) {
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
BaseTextFormField(
hintText: 'Email Address*',
),
SizedBox(height: 20),
],
),
bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24),
bottomSection: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
PrimaryButton(
text: S.of(context).reset_password,
onPressed: () {},
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
],
),
],
),
);
}
}

View file

@ -1 +0,0 @@
export 'auth/welcome_page.dart';

View file

@ -35,7 +35,7 @@ class MarketPlacePage extends StatelessWidget {
children: <Widget>[
SizedBox(height: 20),
MarketPlaceItem(
onTap: () => Navigator.of(context).pushNamed(Routes.cakePayWelcomePage),
onTap: () => Navigator.of(context).pushNamed(Routes.ioniaWelcomePage),
title: S.of(context).cake_pay_title,
subTitle: S.of(context).cake_pay_subtitle,
),

View file

@ -13,10 +13,10 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class CreateAccountPage extends BasePage {
class IoniaCreateAccountPage extends BasePage {
final IoniaViewModel _ioniaViewModel;
CreateAccountPage(this._ioniaViewModel)
IoniaCreateAccountPage(this._ioniaViewModel)
: _emailFocus = FocusNode(),
_emailController = TextEditingController(),
_formKey = GlobalKey<FormState>() {
@ -43,12 +43,12 @@ class CreateAccountPage extends BasePage {
@override
Widget body(BuildContext context) {
reaction((_) => _ioniaViewModel.createUserState, (IoniaCreateState state) {
reaction((_) => _ioniaViewModel.createUserState, (IoniaCreateAccountState state) {
if (state is IoniaCreateStateFailure) {
_onCreateUserFailure(context, state.error);
}
if (state is IoniaCreateStateSuccess) {
_onCreateSuccessful(context);
_onCreateSuccessful(context, _ioniaViewModel);
}
});
@ -135,4 +135,8 @@ void _onCreateUserFailure(BuildContext context, String error) {
});
}
void _onCreateSuccessful(BuildContext context) => Navigator.pushNamed(context, Routes.verifyIoniaOtpPage);
void _onCreateSuccessful(BuildContext context, IoniaViewModel ioniaViewModel) => Navigator.pushNamed(
context,
Routes.ioniaVerifyIoniaOtpPage,
arguments: [ioniaViewModel.email, ioniaViewModel],
);

View file

@ -14,9 +14,9 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class LoginPage extends BasePage {
class IoniaLoginPage extends BasePage {
final IoniaViewModel _ioniaViewModel;
LoginPage(this._ioniaViewModel)
IoniaLoginPage(this._ioniaViewModel)
: _formKey = GlobalKey<FormState>(),
_emailFocus = FocusNode(),
_emailController = TextEditingController() {
@ -45,12 +45,12 @@ class LoginPage extends BasePage {
@override
Widget body(BuildContext context) {
reaction((_) => _ioniaViewModel.createUserState, (IoniaCreateState state) {
reaction((_) => _ioniaViewModel.createUserState, (IoniaCreateAccountState state) {
if (state is IoniaCreateStateFailure) {
_onLoginUserFailure(context, state.error);
}
if (state is IoniaCreateStateSuccess) {
_onLoginSuccessful(context);
_onLoginSuccessful(context, _ioniaViewModel);
}
});
return ScrollableWithBottomSection(
@ -86,17 +86,6 @@ class LoginPage extends BasePage {
SizedBox(
height: 20,
),
InkWell(
onTap: () => Navigator.of(context).pushNamed(Routes.cakePayForgotPasswordPage),
child: Text(
S.of(context).forgot_password,
style: TextStyle(
color: Palette.blueCraiola,
fontSize: 16,
fontWeight: FontWeight.w900,
),
),
)
],
),
],
@ -117,4 +106,8 @@ void _onLoginUserFailure(BuildContext context, String error) {
});
}
void _onLoginSuccessful(BuildContext context) => Navigator.pushNamed(context, Routes.verifyIoniaOtpPage);
void _onLoginSuccessful(BuildContext context, IoniaViewModel ioniaViewModel) => Navigator.pushNamed(
context,
Routes.ioniaVerifyIoniaOtpPage,
arguments: [ioniaViewModel.email, ioniaViewModel],
);

View file

@ -1,18 +1,24 @@
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
class VerifyIoniaOtp extends BasePage {
class IoniaVerifyIoniaOtp extends BasePage {
final IoniaViewModel _ioniaViewModel;
final String _email;
VerifyIoniaOtp(this._ioniaViewModel)
IoniaVerifyIoniaOtp(this._ioniaViewModel, this._email)
: _codeController = TextEditingController(),
_codeFocus = FocusNode() {
_codeController.addListener(() {
@ -43,6 +49,14 @@ class VerifyIoniaOtp extends BasePage {
@override
Widget body(BuildContext context) {
reaction((_) => _ioniaViewModel.otpState, (IoniaOtpState state) {
if (state is IoniaOtpFailure) {
_onOtpFailure(context, state.error);
}
if (state is IoniaOtpSuccess) {
_onOtpSuccessful(context);
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Column(
@ -63,12 +77,11 @@ class VerifyIoniaOtp extends BasePage {
children: [
Text(S.of(context).dont_get_code),
SizedBox(width: 20),
Text(
S.of(context).resend_code,
style: TextStyle(
color: Palette.blueCraiola,
fontSize: 14,
fontWeight: FontWeight.w400,
InkWell(
onTap: () => _ioniaViewModel.createUser(_email),
child: Text(
S.of(context).resend_code,
style: textSmallSemiBold(color: Palette.blueCraiola),
),
),
],
@ -91,9 +104,7 @@ class VerifyIoniaOtp extends BasePage {
textColor: Colors.white,
),
),
SizedBox(
height: 20,
),
SizedBox(height: 20),
],
),
],
@ -101,3 +112,18 @@ class VerifyIoniaOtp extends BasePage {
);
}
}
void _onOtpFailure(BuildContext context, String error) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.verification,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
void _onOtpSuccessful(BuildContext context) =>
Navigator.pushNamedAndRemoveUntil(context, Routes.ioniaManageCardsPage, ModalRoute.withName(Routes.dashboard));

View file

@ -8,10 +8,10 @@ import 'package:flutter/src/widgets/framework.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:mobx/mobx.dart';
class WelcomePage extends BasePage {
class IoniaWelcomePage extends BasePage {
final IoniaViewModel _ioniaViewModel;
WelcomePage(this._ioniaViewModel);
IoniaWelcomePage(this._ioniaViewModel);
@override
Color get titleColor => Colors.black;
@ -32,7 +32,7 @@ class WelcomePage extends BasePage {
Widget body(BuildContext context) {
reaction((_) => _ioniaViewModel.isLoggedIn, (bool state) {
if (state) {
// TODO:: Navigate to main page
Navigator.pushReplacementNamed(context, Routes.ioniaDebitCardPage);
}
});
return Padding(
@ -69,7 +69,7 @@ class WelcomePage extends BasePage {
children: <Widget>[
PrimaryButton(
text: S.of(context).create_account,
onPressed: () => Navigator.of(context).pushNamed(Routes.cakePayCreateAccountPage),
onPressed: () => Navigator.of(context).pushNamed(Routes.ioniaCreateAccountPage),
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
@ -87,7 +87,7 @@ class WelcomePage extends BasePage {
),
SizedBox(height: 8),
InkWell(
onTap: () => Navigator.of(context).pushNamed(Routes.cakePayLoginPage),
onTap: () => Navigator.of(context).pushNamed(Routes.ioniaLoginPage),
child: Text(
S.of(context).login,
style: TextStyle(

View file

@ -0,0 +1,114 @@
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:mobx/mobx.dart';
class IoniaActivateDebitCardPage extends BasePage {
final IoniaViewModel _ioniaViewModel;
IoniaActivateDebitCardPage(this._ioniaViewModel);
@override
Widget middle(BuildContext context) {
return Text(
S.current.debit_card,
style: TextStyle(
fontSize: 22,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
),
);
}
@override
Widget body(BuildContext context) {
reaction((_) => _ioniaViewModel.createCardState, (IoniaCreateCardState state) {
if (state is IoniaCreateCardFailure) {
_onCreateCardFailure(context, state.error);
}
if (state is IoniaCreateCardSuccess) {
_onCreateCardSuccess(context);
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
SizedBox(height: 16),
Text(S.of(context).debit_card_terms),
SizedBox(height: 24),
Text(S.of(context).please_reference_document),
SizedBox(height: 40),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
children: [
TextIconButton(
label: S.current.cardholder_agreement,
onTap: () {},
),
SizedBox(
height: 24,
),
TextIconButton(
label: S.current.e_sign_consent,
onTap: () {},
),
],
),
),
],
),
),
bottomSection: LoadingPrimaryButton(
onPressed: () {
_ioniaViewModel.createCard();
},
isLoading: _ioniaViewModel.createCardState is IoniaCreateCardLoading,
text: S.of(context).agree_and_continue,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
);
}
}
void _onCreateCardFailure(BuildContext context, String errorMessage) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.error,
alertContent: errorMessage,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
void _onCreateCardSuccess(BuildContext context) {
Navigator.pushNamed(
context,
Routes.ioniaDebitCardPage,
);
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: 'Congratulations!',
alertContent: 'You now have a debit card',
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
}

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/discount_badge.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
@ -13,7 +14,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:cake_wallet/generated/i18n.dart';
class BuyGiftCardDetailPage extends StatelessWidget {
class IoniaBuyGiftCardDetailPage extends StatelessWidget {
ThemeBase get currentTheme => getIt.get<SettingsStore>().currentTheme;
Color get backgroundLightColor => Colors.white;
@ -175,24 +176,11 @@ class BuyGiftCardDetailPage extends StatelessWidget {
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).how_to_use_card,
style: textMediumSemiBold(
color: Theme.of(context).primaryTextTheme.title.color,
),
),
Icon(
Icons.chevron_right_rounded,
color: Theme.of(context).primaryTextTheme.title.color,
),
],
),
child: TextIconButton(
label: S.of(context).how_to_use_card,
onTap: () {},
),
)
),
],
),
bottomSection: Column(
@ -308,7 +296,7 @@ class TipButton extends StatelessWidget {
SizedBox(height: 4),
Text(
subTitle,
style: textXSmallSemiBold(color: Palette.gray),
style: textXxSmallSemiBold(color: Palette.gray),
),
]
],

View file

@ -11,8 +11,8 @@ import 'package:flutter/services.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/generated/i18n.dart';
class BuyGiftCardPage extends BasePage {
BuyGiftCardPage()
class IoniaBuyGiftCardPage extends BasePage {
IoniaBuyGiftCardPage()
: _amountFieldFocus = FocusNode(),
_amountController = TextEditingController();
@override
@ -143,14 +143,13 @@ class BuyGiftCardPage extends BasePage {
Padding(
padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
onPressed: () =>Navigator.of(context).pushNamed(Routes.buyGiftCardDetailPage),
onPressed: () => Navigator.of(context).pushNamed(Routes.ioniaBuyGiftCardDetailPage),
text: S.of(context).continue_text,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
),
SizedBox(height: 20),
SizedBox(height: 10)
SizedBox(height: 30),
],
),
),

View file

@ -0,0 +1,382 @@
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/text_icon_button.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class IoniaDebitCardPage extends BasePage {
final IoniaViewModel _ioniaViewModel;
IoniaDebitCardPage(this._ioniaViewModel);
@override
Widget middle(BuildContext context) {
return Text(
S.current.debit_card,
style: TextStyle(
fontSize: 22,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
),
);
}
@override
Widget body(BuildContext context) {
return Observer(
builder: (_) {
final cardState = _ioniaViewModel.cardState;
if (cardState is IoniaFetchingCard) {
return Center(child: CircularProgressIndicator());
}
if (cardState is IoniaCardSuccess) {
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
_IoniaDebitCard(
cardInfo: cardState.card,
)
],
),
),
bottomSection: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
'If asked for a billing address, provide your shipping address',
style: textSmall(color: Theme.of(context).textTheme.display1.color),
textAlign: TextAlign.center,
),
),
SizedBox(height: 24),
PrimaryButton(
text: 'Order Physical Card',
onPressed: () {},
color: Color(0xffE9F2FC),
textColor: Theme.of(context).textTheme.display2.color,
),
SizedBox(height: 8),
PrimaryButton(
text: 'Add Value',
onPressed: () {},
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
SizedBox(height: 16)
],
),
);
}
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
_IoniaDebitCard(isCardSample: true),
SizedBox(height: 40),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
children: [
TextIconButton(
label: S.current.how_to_use_card,
onTap: () => _showHowToUseCard(context),
),
SizedBox(
height: 24,
),
TextIconButton(
label: S.current.frequently_asked_questions,
onTap: () {},
),
],
),
),
SizedBox(height: 50),
Container(
padding: EdgeInsets.all(20),
margin: EdgeInsets.all(8),
width: double.infinity,
decoration: BoxDecoration(
color: Color.fromRGBO(233, 242, 252, 1),
borderRadius: BorderRadius.circular(20),
),
child: RichText(
text: TextSpan(
text: 'Get a',
style: textMedium(color: Theme.of(context).textTheme.display2.color),
children: [
TextSpan(
text: ' digital and physical prepaid debit card',
style: textMediumBold(color: Theme.of(context).textTheme.display2.color),
),
TextSpan(
text: ' that you can reload with digital currencies. No additional information needed!')
])),
),
],
),
),
bottomSectionPadding: EdgeInsets.symmetric(
horizontal: 16,
vertical: 32,
),
bottomSection: PrimaryButton(
text: 'Activate',
onPressed: () => _showHowToUseCard(context, activate: true),
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
);
},
);
}
void _showHowToUseCard(BuildContext context, {bool activate = false}) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertBackground(
child: Material(
color: Colors.transparent,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: 10),
Container(
padding: EdgeInsets.only(top: 24, left: 24, right: 24),
margin: EdgeInsets.all(24),
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
borderRadius: BorderRadius.circular(30),
),
child: Column(
children: [
Text(
'How to use this card',
style: textLargeSemiBold(
color: Theme.of(context).textTheme.body1.color,
),
),
SizedBox(height: 24),
Align(
alignment: Alignment.bottomLeft,
child: Text(
'Sign up for the card and accept the terms.',
style: textSmallSemiBold(
color: Theme.of(context).textTheme.display2.color,
),
),
),
SizedBox(height: 24),
_TitleSubtitleTile(
title: 'Add prepaid funds to the cards (up to \$1000)',
subtitle:
'Funds are converted to USD when the held in the prepaid account, not in digital currencies.',
),
SizedBox(height: 21),
_TitleSubtitleTile(
title: 'Use the digital card online or with contactless payment methods.',
subtitle: 'Optionally order a physical card.',
),
SizedBox(height: 35),
PrimaryButton(
onPressed: () => activate
? Navigator.pushNamed(context, Routes.ioniaActivateDebitCardPage)
: Navigator.pop(context),
text: 'Got it!',
color: Color.fromRGBO(233, 242, 252, 1),
textColor: Theme.of(context).textTheme.display2.color,
),
SizedBox(height: 21),
],
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: Container(
margin: EdgeInsets.only(bottom: 40),
child: CircleAvatar(
child: Icon(
Icons.close,
color: Colors.black,
),
backgroundColor: Colors.white,
),
),
)
],
),
),
);
});
}
}
class _IoniaDebitCard extends StatefulWidget {
final bool isCardSample;
final IoniaVirtualCard cardInfo;
const _IoniaDebitCard({
Key key,
this.isCardSample = false,
this.cardInfo,
}) : super(key: key);
@override
_IoniaDebitCardState createState() => _IoniaDebitCardState();
}
class _IoniaDebitCardState extends State<_IoniaDebitCard> {
bool _showDetails = false;
void _toggleVisibility() {
setState(() => _showDetails = !_showDetails);
}
String _formatPan(String pan) {
if (pan == null) return '';
return pan.replaceAllMapped(RegExp(r'.{4}'), (match) => '${match.group(0)} ');
}
@override
Widget build(BuildContext context) {
final last4 = widget.isCardSample ? '0000' : widget.cardInfo.pan.substring(widget.cardInfo.pan.length - 5);
final spendLimit = widget.isCardSample ? '10000' : widget.cardInfo.spendLimit.toStringAsFixed(2);
return Container(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 19),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
gradient: LinearGradient(
colors: [
Theme.of(context).primaryTextTheme.subhead.color,
Theme.of(context).primaryTextTheme.subhead.decorationColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.current.cakepay_prepaid_card,
style: textSmall(),
),
Image.asset(
'assets/images/mastercard.png',
width: 54,
),
],
),
Text(
widget.isCardSample ? 'up to \$$spendLimit' : '\$$spendLimit',
style: textXLargeSemiBold(),
),
SizedBox(height: 16),
Text(
_showDetails ? _formatPan(widget.cardInfo.pan) : '**** **** **** $last4',
style: textMediumSemiBold(),
),
SizedBox(height: 32),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (widget.isCardSample)
Text(
S.current.no_id_needed,
style: textMediumBold(),
)
else ...[
Column(
children: [
Text(
'CVV',
style: textXSmallSemiBold(),
),
SizedBox(height: 4),
Text(
_showDetails ? widget.cardInfo.cvv : '***',
style: textMediumSemiBold(),
)
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Expires',
style: textXSmallSemiBold(),
),
SizedBox(height: 4),
Text(
'${widget.cardInfo.expirationMonth ?? 'MM'}/${widget.cardInfo.expirationYear ?? 'YY'}',
style: textMediumSemiBold(),
)
],
),
]
],
),
if (!widget.isCardSample) ...[
SizedBox(height: 8),
Center(
child: InkWell(
onTap: () => _toggleVisibility(),
child: Text(
_showDetails ? 'Hide Details' : 'Show Details',
style: textSmall(),
),
),
),
],
],
),
);
}
}
class _TitleSubtitleTile extends StatelessWidget {
final String title;
final String subtitle;
const _TitleSubtitleTile({
Key key,
@required this.title,
@required this.subtitle,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: textSmallSemiBold(color: Theme.of(context).textTheme.display2.color),
),
SizedBox(height: 4),
Text(
subtitle,
style: textSmall(color: Theme.of(context).textTheme.display2.color),
),
],
);
}
}

View file

@ -1,13 +1,18 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/cake_pay/widgets/card_menu.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_menu.dart';
import 'package:cake_wallet/src/widgets/market_place_item.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class ManageCardsPage extends BasePage {
class IoniaManageCardsPage extends BasePage {
final IoniaViewModel _ioniaViewModel;
IoniaManageCardsPage(this._ioniaViewModel);
@override
Color get backgroundLightColor => currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@ -39,6 +44,29 @@ class ManageCardsPage extends BasePage {
@override
Widget get endDrawer => CardMenu();
@override
Widget leading(BuildContext context) {
final _backButton = Icon(
Icons.arrow_back_ios,
color: titleColor ?? Theme.of(context).primaryTextTheme.title.color,
size: 16,
);
return SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Navigator.pushReplacementNamed(context, Routes.dashboard),
child: _backButton),
),
);
}
@override
Widget middle(BuildContext context) {
return Text(
@ -60,7 +88,7 @@ class ManageCardsPage extends BasePage {
children: [
_TrailingIcon(
asset: 'assets/images/card.png',
onPressed: () {},
onPressed: () => Navigator.pushNamed(context, Routes.ioniaDebitCardPage),
),
SizedBox(width: 16),
_TrailingIcon(
@ -127,7 +155,7 @@ class ManageCardsPage extends BasePage {
return MarketPlaceItem(
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 12),
logoUrl: '',
onTap: ()=>Navigator.of(context).pushNamed(Routes.buyGiftCardPage),
onTap: () => Navigator.of(context).pushNamed(Routes.ioniaBuyGiftCardPage),
title: 'Amazon',
subTitle: 'Onlin',
hasDiscount: true,

View file

@ -0,0 +1,9 @@
export 'auth/ionia_welcome_page.dart';
export 'auth/ionia_create_account_page.dart';
export 'auth/ionia_login_page.dart';
export 'auth/ionia_verify_otp_page.dart';
export 'cards/ionia_activate_debit_card_page.dart';
export 'cards/ionia_buy_card_detail_page.dart';
export 'cards/ionia_manage_cards_page.dart';
export 'cards/ionia_debit_card_page.dart';
export 'cards/ionia_buy_gift_card.dart';

View file

@ -0,0 +1,35 @@
import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart';
class TextIconButton extends StatelessWidget {
final String label;
final VoidCallback onTap;
const TextIconButton({
Key key,
this.label,
this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return
InkWell(
onTap: onTap,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: textMediumSemiBold(
color: Theme.of(context).primaryTextTheme.title.color,
),
),
Icon(
Icons.chevron_right_rounded,
color: Theme.of(context).primaryTextTheme.title.color,
),
],
),
);
}
}

View file

@ -2,9 +2,13 @@ import 'package:flutter/material.dart';
const latoFont = "Lato";
TextStyle textXSmall({Color color}) => _cakeRegular(10, color);
TextStyle textXxSmall({Color color}) => _cakeRegular(10, color);
TextStyle textXSmallSemiBold({Color color}) => _cakeSemiBold(10, color);
TextStyle textXxSmallSemiBold({Color color}) => _cakeSemiBold(10, color);
TextStyle textXSmall({Color color}) => _cakeRegular(12, color);
TextStyle textXSmallSemiBold({Color color}) => _cakeSemiBold(12, color);
TextStyle textSmall({Color color}) => _cakeRegular(14, color);
@ -12,6 +16,8 @@ TextStyle textSmallSemiBold({Color color}) => _cakeSemiBold(14, color);
TextStyle textMedium({Color color}) => _cakeRegular(16, color);
TextStyle textMediumBold({Color color}) => _cakeBold(16, color);
TextStyle textMediumSemiBold({Color color}) => _cakeSemiBold(22, color);
TextStyle textLarge({Color color}) => _cakeRegular(18, color);
@ -28,6 +34,12 @@ TextStyle _cakeRegular(double size, Color color) => _textStyle(
color: color,
);
TextStyle _cakeBold(double size, Color color) => _textStyle(
size: size,
fontWeight: FontWeight.w900,
color: color,
);
TextStyle _cakeSemiBold(double size, Color color) => _textStyle(
size: size,
fontWeight: FontWeight.w700,

View file

@ -1,5 +1,6 @@
import 'package:cake_wallet/ionia/ionia.dart';
import 'package:cake_wallet/ionia/ionia_create_state.dart';
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
import 'package:mobx/mobx.dart';
part 'ionia_view_model.g.dart';
@ -8,18 +9,25 @@ class IoniaViewModel = IoniaViewModelBase with _$IoniaViewModel;
abstract class IoniaViewModelBase with Store {
IoniaViewModelBase({this.ioniaService})
: createUserState = IoniaCreateStateSuccess(),
otpState = IoniaOtpSendDisabled() {
otpState = IoniaOtpSendDisabled(), cardState = IoniaNoCardState() {
_getCard();
_getAuthStatus().then((value) => isLoggedIn = value);
}
final IoniaService ioniaService;
@observable
IoniaCreateState createUserState;
IoniaCreateAccountState createUserState;
@observable
IoniaOtpState otpState;
@observable
IoniaCreateCardState createCardState;
@observable
IoniaFetchCardState cardState;
@observable
String email;
@ -55,4 +63,31 @@ abstract class IoniaViewModelBase with Store {
Future<bool> _getAuthStatus() async {
return await ioniaService.isLogined();
}
@action
Future<IoniaVirtualCard> createCard() async {
createCardState = IoniaCreateCardLoading();
try {
final card = await ioniaService.createCard();
createCardState = IoniaCreateCardSuccess();
return card;
} catch (e) {
final error = e as Exception;
createCardState = IoniaCreateCardFailure(error: error.toString());
}
return null;
}
Future<void> _getCard() async {
cardState = IoniaFetchingCard();
try {
final card = await ioniaService.getCard();
cardState = IoniaCardSuccess(card: card);
} catch (_) {
cardState = IoniaFetchCardFailure();
}
}
}

View file

@ -51,6 +51,7 @@ dependencies:
flushbar: ^1.10.4
archive: ^2.0.13
cryptography: ^1.4.0
cached_network_image: ^2.0.0
file_picker: ^3.0.0-nullsafety.2
unorm_dart: ^0.2.0
permission_handler: ^5.0.1+1

View file

@ -551,5 +551,14 @@
"verification": "Verification",
"fill_code": "Please fill in the verification code provided to your email",
"dont_get_code": "Don't get code",
"resend_code": "Please resend it"
"resend_code": "Please resend it",
"debit_card": "Debit Card",
"cakepay_prepaid_card": "CakePay Prepaid Debit Card",
"no_id_needed": "No ID needed!",
"frequently_asked_questions": "Frequently asked questions",
"debit_card_terms": "The storage and usage of your payment card number (and credentials corresponding to your payment card number) in this digital wallet are subject to the Terms and Conditions of the applicable cardholder agreement with the payment card issuer, as in effect from time to time.",
"please_reference_document": "Please reference the documents below for more information.",
"cardholder_agreement": "Cardholder Agreement",
"e_sign_consent": "E-Sign Consent",
"agree_and_continue": "Agree & Continue"
}