Ionia manage card UI (#374)

* design ui for cakepay

* Add manage cards page ui

* create auth ui for ionia

* add authentication logic

* implement user create card
This commit is contained in:
Godwin Asuquo 2022-06-15 14:26:19 +03:00 committed by GitHub
parent 5314d4986c
commit de0ca1de6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 2617 additions and 80 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/images/card.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

BIN
assets/images/filter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
assets/images/profile.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

BIN
assets/images/wifi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -169,13 +169,6 @@ packages:
relative: true
source: path
version: "0.0.1"
cw_monero:
dependency: "direct main"
description:
path: "../cw_monero"
relative: true
source: path
version: "0.0.1"
dart_style:
dependency: transitive
description:

View file

@ -0,0 +1,11 @@
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/generated/i18n.dart';
class EmailValidator extends TextValidator {
EmailValidator()
: super(
errorMessage: 'Invalid email address',
pattern:
'^[^@]+@[^@]+\.[^@]+',
);
}

View file

@ -1,11 +1,15 @@
import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/wake_lock.dart';
import 'package:cake_wallet/ionia/ionia.dart';
import 'package:cake_wallet/ionia/ionia_api.dart';
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/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';
import 'package:cake_wallet/core/backup_service.dart';
import 'package:cw_core/wallet_service.dart';
@ -641,5 +645,36 @@ Future setup(
getIt.registerFactory(() => AddressResolver(yatService: getIt.get<YatService>()));
getIt.registerFactory(() => IoniaApi());
getIt.registerFactory<IoniaService>(
() => IoniaService(getIt.get<FlutterSecureStorage>(), getIt.get<IoniaApi>()));
getIt.registerFactory(() => IoniaViewModel(ioniaService: getIt.get<IoniaService>()));
getIt.registerFactory(() => IoniaCreateAccountPage(getIt.get<IoniaViewModel>()));
getIt.registerFactory(() => IoniaLoginPage(getIt.get<IoniaViewModel>()));
getIt.registerFactoryParam<IoniaVerifyIoniaOtp, List, void>((List args, _) {
final email = args.first as String;
final ioniaViewModel = args[1] as IoniaViewModel;
return IoniaVerifyIoniaOtp(ioniaViewModel, email);
});
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>()));
_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

@ -0,0 +1,56 @@
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
import 'package:flutter/material.dart';
abstract class IoniaCreateAccountState {}
class IoniaCreateStateSuccess extends IoniaCreateAccountState {}
class IoniaCreateStateLoading extends IoniaCreateAccountState {}
class IoniaCreateStateFailure extends IoniaCreateAccountState {
IoniaCreateStateFailure({@required this.error});
final String error;
}
abstract class IoniaOtpState {}
class IoniaOtpValidating extends IoniaOtpState {}
class IoniaOtpSuccess extends IoniaOtpState {}
class IoniaOtpSendDisabled extends IoniaOtpState {}
class IoniaOtpSendEnabled extends IoniaOtpState {}
class IoniaOtpFailure extends IoniaOtpState {
IoniaOtpFailure({@required this.error});
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

@ -70,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;
@ -402,6 +403,34 @@ Route<dynamic> createRoute(RouteSettings settings) {
getIt.get<UnspentCoinsDetailsPage>(
param1: args));
case Routes.ioniaWelcomePage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaWelcomePage>());
case Routes.ioniaLoginPage:
return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaLoginPage>());
case Routes.ioniaCreateAccountPage:
return CupertinoPageRoute<void>( builder: (_) => getIt.get<IoniaCreateAccountPage>());
case Routes.ioniaManageCardsPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaManageCardsPage>());
case Routes.ioniaBuyGiftCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaBuyGiftCardPage>());
case Routes.ioniaBuyGiftCardDetailPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaBuyGiftCardDetailPage>());
case Routes.ioniaVerifyIoniaOtpPage:
final args = settings.arguments as List;
return CupertinoPageRoute<void>(builder: (_) =>getIt.get<IoniaVerifyIoniaOtp>(param1: args));
case Routes.ioniaDebitCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaDebitCardPage>());
case Routes.ioniaActivateDebitCardPage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaActivateDebitCardPage>());
default:
return MaterialPageRoute<void>(
builder: (_) => Scaffold(

View file

@ -60,4 +60,14 @@ class Routes {
static const moneroRestoreWalletFromWelcome = '/monero_restore_wallet';
static const moneroNewWalletFromWelcome = '/monero_new_wallet';
static const addressPage = '/address_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,8 +1,8 @@
import 'dart:async';
import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/yat/yat_popup.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/themes/theme_base.dart';
@ -14,19 +14,15 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/action_button.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/router.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
class DashboardPage extends BasePage {
DashboardPage({
@ -85,7 +81,7 @@ class DashboardPage extends BasePage {
final DashboardViewModel walletViewModel;
final WalletAddressListViewModel addressListViewModel;
final controller = PageController(initialPage: 0);
final controller = PageController(initialPage: 1);
var pages = <Widget>[];
bool _isEffectsInstalled = false;
@ -221,7 +217,7 @@ class DashboardPage extends BasePage {
if (_isEffectsInstalled) {
return;
}
pages.add(MarketPlacePage());
pages.add(balancePage);
pages.add(TransactionsPage(dashboardViewModel: walletViewModel));
_isEffectsInstalled = true;

View file

@ -0,0 +1,52 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/market_place_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class MarketPlacePage extends StatelessWidget {
final _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: RawScrollbar(
thumbColor: Colors.white.withOpacity(0.15),
radius: Radius.circular(20),
isAlwaysShown: true,
thickness: 2,
controller: _scrollController,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 50),
Text(
S.of(context).market_place,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.display3.backgroundColor,
),
),
Expanded(
child: ListView(
controller: _scrollController,
children: <Widget>[
SizedBox(height: 20),
MarketPlaceItem(
onTap: () => Navigator.of(context).pushNamed(Routes.ioniaWelcomePage),
title: S.of(context).cake_pay_title,
subTitle: S.of(context).cake_pay_subtitle,
),
],
),
),
],
),
),
),
);
}
}

View file

@ -0,0 +1,143 @@
import 'package:cake_wallet/core/email_validator.dart';
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/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/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 IoniaCreateAccountPage extends BasePage {
IoniaCreateAccountPage(this._ioniaViewModel)
: _emailFocus = FocusNode(),
_emailController = TextEditingController(),
_formKey = GlobalKey<FormState>() {
_emailController.text = _ioniaViewModel.email;
_emailController.addListener(() => _ioniaViewModel.email = _emailController.text);
}
final IoniaViewModel _ioniaViewModel;
final GlobalKey<FormState> _formKey;
final FocusNode _emailFocus;
final TextEditingController _emailController;
@override
Widget middle(BuildContext context) {
return Text(
S.current.sign_up,
style: TextStyle(
fontSize: 22,
fontFamily: 'Lato',
color: Theme.of(context).accentTextTheme.display4.backgroundColor,
fontWeight: FontWeight.w900,
),
);
}
@override
Widget body(BuildContext context) {
reaction((_) => _ioniaViewModel.createUserState, (IoniaCreateAccountState state) {
if (state is IoniaCreateStateFailure) {
_onCreateUserFailure(context, state.error);
}
if (state is IoniaCreateStateSuccess) {
_onCreateSuccessful(context, _ioniaViewModel);
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Form(
key: _formKey,
child: BaseTextFormField(
hintText: S.of(context).email_address,
focusNode: _emailFocus,
validator: EmailValidator(),
controller: _emailController,
),
),
bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24),
bottomSection: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).create_account,
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
await _ioniaViewModel.createUser(_emailController.text);
},
isLoading: _ioniaViewModel.createUserState is IoniaCreateStateLoading,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
),
SizedBox(
height: 20,
),
RichText(
textAlign: TextAlign.center,
text: TextSpan(
text: S.of(context).agree_to,
style: TextStyle(
color: Color(0xff7A93BA),
fontSize: 12,
fontFamily: 'Lato',
),
children: [
TextSpan(
text: S.of(context).settings_terms_and_conditions,
style: TextStyle(
color: Theme.of(context).accentTextTheme.body2.color,
fontWeight: FontWeight.w700,
),
),
TextSpan(text: ' ${S.of(context).and} '),
TextSpan(
text: S.of(context).privacy_policy,
style: TextStyle(
color: Theme.of(context).accentTextTheme.body2.color,
fontWeight: FontWeight.w700,
),
),
TextSpan(text: ' ${S.of(context).by_cake_pay}'),
],
),
),
],
),
],
),
);
}
void _onCreateUserFailure(BuildContext context, String error) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.create_account,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
void _onCreateSuccessful(BuildContext context, IoniaViewModel ioniaViewModel) => Navigator.pushNamed(
context,
Routes.ioniaVerifyIoniaOtpPage,
arguments: [ioniaViewModel.email, ioniaViewModel],
);
}

View file

@ -0,0 +1,112 @@
import 'package:cake_wallet/core/email_validator.dart';
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/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/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 IoniaLoginPage extends BasePage {
IoniaLoginPage(this._ioniaViewModel)
: _formKey = GlobalKey<FormState>(),
_emailController = TextEditingController() {
_emailController.text = _ioniaViewModel.email;
_emailController.addListener(() => _ioniaViewModel.email = _emailController.text);
}
final GlobalKey<FormState> _formKey;
final IoniaViewModel _ioniaViewModel;
@override
Color get titleColor => Colors.black;
final TextEditingController _emailController;
@override
Widget middle(BuildContext context) {
return Text(
S.current.login,
style: TextStyle(
fontSize: 22,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
),
);
}
@override
Widget body(BuildContext context) {
reaction((_) => _ioniaViewModel.createUserState, (IoniaCreateAccountState state) {
if (state is IoniaCreateStateFailure) {
_onLoginUserFailure(context, state.error);
}
if (state is IoniaCreateStateSuccess) {
_onLoginSuccessful(context, _ioniaViewModel);
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Form(
key: _formKey,
child: BaseTextFormField(
hintText: S.of(context).email_address,
validator: EmailValidator(),
controller: _emailController,
),
),
bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24),
bottomSection: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).login,
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
await _ioniaViewModel.createUser(_emailController.text);
},
isLoading: _ioniaViewModel.createUserState is IoniaCreateStateLoading,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
),
SizedBox(
height: 20,
),
],
),
],
),
);
}
void _onLoginUserFailure(BuildContext context, String error) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.login,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
void _onLoginSuccessful(BuildContext context, IoniaViewModel ioniaViewModel) => Navigator.pushNamed(
context,
Routes.ioniaVerifyIoniaOtpPage,
arguments: [ioniaViewModel.email, ioniaViewModel],
);
}

View file

@ -0,0 +1,132 @@
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 IoniaVerifyIoniaOtp extends BasePage {
IoniaVerifyIoniaOtp(this._ioniaViewModel, this._email)
: _codeController = TextEditingController(),
_codeFocus = FocusNode() {
_codeController.addListener(() {
final otp = _codeController.text;
_ioniaViewModel.otp = otp;
if (otp.length > 3) {
_ioniaViewModel.otpState = IoniaOtpSendEnabled();
} else {
_ioniaViewModel.otpState = IoniaOtpSendDisabled();
}
});
}
final IoniaViewModel _ioniaViewModel;
final String _email;
@override
Widget middle(BuildContext context) {
return Text(
S.current.verification,
style: TextStyle(
fontSize: 22,
fontFamily: 'Lato',
color: Theme.of(context).accentTextTheme.display4.backgroundColor,
fontWeight: FontWeight.w900,
),
);
}
final TextEditingController _codeController;
final FocusNode _codeFocus;
@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(
children: [
BaseTextFormField(
hintText: S.of(context).enter_code,
focusNode: _codeFocus,
controller: _codeController,
),
SizedBox(height: 14),
Text(
S.of(context).fill_code,
style: TextStyle(color: Color(0xff7A93BA), fontSize: 12),
),
SizedBox(height: 34),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(S.of(context).dont_get_code),
SizedBox(width: 20),
InkWell(
onTap: () => _ioniaViewModel.createUser(_email),
child: Text(
S.of(context).resend_code,
style: textSmallSemiBold(color: Palette.blueCraiola),
),
),
],
),
],
),
bottomSectionPadding: EdgeInsets.symmetric(vertical: 36, horizontal: 24),
bottomSection: Column(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).continue_text,
onPressed: () async => await _ioniaViewModel.verifyEmail(_codeController.text),
isDisabled: _ioniaViewModel.otpState is IoniaOtpSendDisabled,
isLoading: _ioniaViewModel.otpState is IoniaOtpValidating,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
),
SizedBox(height: 20),
],
),
],
),
);
}
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

@ -0,0 +1,104 @@
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/primary_button.dart';
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:mobx/mobx.dart';
class IoniaWelcomePage extends BasePage {
IoniaWelcomePage(this._ioniaViewModel);
@override
Widget middle(BuildContext context) {
return Text(
S.current.welcome_to_cakepay,
style: TextStyle(
fontSize: 22,
fontFamily: 'Lato',
color: Theme.of(context).accentTextTheme.display4.backgroundColor,
fontWeight: FontWeight.w900,
),
);
}
final IoniaViewModel _ioniaViewModel;
@override
Widget body(BuildContext context) {
reaction((_) => _ioniaViewModel.isLoggedIn, (bool state) {
if (state) {
Navigator.pushReplacementNamed(context, Routes.ioniaDebitCardPage);
}
});
return Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
SizedBox(height: 100),
Text(
S.of(context).about_cake_pay,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color,
),
),
SizedBox(height: 20),
Text(
S.of(context).cake_pay_account_note,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w400,
fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color,
),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
PrimaryButton(
text: S.of(context).create_account,
onPressed: () => Navigator.of(context).pushNamed(Routes.ioniaCreateAccountPage),
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
SizedBox(
height: 16,
),
Text(
S.of(context).already_have_account,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color,
),
),
SizedBox(height: 8),
InkWell(
onTap: () => Navigator.of(context).pushNamed(Routes.ioniaLoginPage),
child: Text(
S.of(context).login,
style: TextStyle(
color: Palette.blueCraiola,
fontSize: 16,
fontWeight: FontWeight.w900,
),
),
)
],
)
],
),
);
}
}

View file

@ -0,0 +1,116 @@
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 {
IoniaActivateDebitCardPage(this._ioniaViewModel);
final IoniaViewModel _ioniaViewModel;
@override
Widget middle(BuildContext context) {
return Text(
S.current.debit_card,
style: TextStyle(
fontSize: 22,
color: Theme.of(context).accentTextTheme.display4.backgroundColor,
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: S.of(context).congratulations,
alertContent: S.of(context).you_now_have_debit_card,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
}
}

View file

@ -0,0 +1,322 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/confirm_modal.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';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/typography.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class IoniaBuyGiftCardDetailPage extends StatelessWidget {
ThemeBase get currentTheme => getIt.get<SettingsStore>().currentTheme;
Color get backgroundLightColor => Colors.white;
Color get backgroundDarkColor => PaletteDark.backgroundColor;
void onClose(BuildContext context) => Navigator.of(context).pop();
Widget leading(BuildContext context) {
if (ModalRoute.of(context).isFirst) {
return null;
}
final _backButton = Icon(
Icons.arrow_back_ios,
color: Theme.of(context).primaryTextTheme.title.color,
size: 16,
);
return Padding(
padding: const EdgeInsets.only(left: 10.0),
child: SizedBox(
height: 37,
width: 37,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => onClose(context),
child: _backButton),
),
),
);
}
Widget middle(BuildContext context) {
return Text(
'AppleBees',
style: TextStyle(
fontSize: 22,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
),
);
}
@override
Widget build(BuildContext context) {
final _backgroundColor = currentTheme.type == ThemeType.dark ? backgroundDarkColor : backgroundLightColor;
return Scaffold(
backgroundColor: _backgroundColor,
body: ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Column(
children: [
SizedBox(height: 60),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [leading(context), middle(context), DiscountBadge()],
),
SizedBox(height: 36),
Container(
padding: EdgeInsets.symmetric(vertical: 24),
margin: EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
gradient: LinearGradient(
colors: [
Theme.of(context).primaryTextTheme.subhead.color,
Theme.of(context).primaryTextTheme.subhead.decorationColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Column(
children: [
Text(
S.of(context).gift_card_amount,
style: textSmall(),
),
SizedBox(height: 4),
Text(
'\$1000.12',
style: textXLargeSemiBold(),
),
SizedBox(height: 24),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).bill_amount,
style: textSmall(),
),
SizedBox(height: 4),
Text(
'\$1000.00',
style: textLargeSemiBold(),
),
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
S.of(context).tip,
style: textSmall(),
),
SizedBox(height: 4),
Text(
'\$1000.00',
style: textLargeSemiBold(),
),
],
),
],
),
),
SizedBox(height: 16),
Divider(),
SizedBox(height: 16),
Text(
S.of(context).you_pay,
style: textSmall(),
),
SizedBox(height: 4),
Text(
'22.3435345000 XMR',
style: textLargeSemiBold(),
),
],
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
S.of(context).tip,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color,
fontWeight: FontWeight.w700,
fontSize: 14,
),
),
SizedBox(height: 4),
TipButtonGroup()
],
),
),
SizedBox(height: 20),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: TextIconButton(
label: S.of(context).how_to_use_card,
onTap: () {},
),
),
],
),
bottomSection: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
onPressed: () => purchaseCard(context),
text: S.of(context).purchase_gift_card,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
),
),
SizedBox(height: 8),
Text(S.of(context).settings_terms_and_conditions,
style: textMediumSemiBold(
color: Theme.of(context).primaryTextTheme.body1.color,
).copyWith(fontSize: 12)),
SizedBox(height: 16)
],
),
),
);
}
void purchaseCard(BuildContext context) {
showPopUp<void>(
context: context,
builder: (_) {
return IoniaConfirmModal(
alertTitle: S.of(context).confirm_sending,
alertContent: SizedBox(
//Todo:: substitute this widget with modal content
height: 200,
),
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
leftActionColor: Color(0xffFF6600),
rightActionColor: Theme.of(context).accentTextTheme.body2.color,
actionRightButton: () async {
Navigator.of(context).pop();
},
actionLeftButton: () => Navigator.of(context).pop());
});
}
}
class TipButtonGroup extends StatefulWidget {
const TipButtonGroup({
Key key,
}) : super(key: key);
@override
_TipButtonGroupState createState() => _TipButtonGroupState();
}
class _TipButtonGroupState extends State<TipButtonGroup> {
String selectedTip;
bool _isSelected(String value) {
return selectedTip == value;
}
@override
Widget build(BuildContext context) {
return Row(
children: [
TipButton(
isSelected: _isSelected('299'),
caption: '\$10',
subTitle: '%299',
),
SizedBox(width: 4),
TipButton(
caption: '\$10',
subTitle: '%299',
),
SizedBox(width: 4),
TipButton(
isSelected: _isSelected('299'),
caption: '\$10',
subTitle: '%299',
),
SizedBox(width: 4),
TipButton(
isSelected: _isSelected('299'),
caption: S.of(context).custom,
),
],
);
}
}
class TipButton extends StatelessWidget {
final String caption;
final String subTitle;
final bool isSelected;
final void Function(int, bool) onTap;
const TipButton({
@required this.caption,
this.subTitle,
this.onTap,
this.isSelected = false,
});
@override
Widget build(BuildContext context) {
return Container(
height: 49,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(caption, style: textSmallSemiBold(color: Theme.of(context).primaryTextTheme.title.color)),
if (subTitle != null) ...[
SizedBox(height: 4),
Text(
subTitle,
style: textXxSmallSemiBold(color: Palette.gray),
),
]
],
),
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Color.fromRGBO(242, 240, 250, 1),
gradient: isSelected
? LinearGradient(
colors: [
Theme.of(context).primaryTextTheme.subhead.color,
Theme.of(context).primaryTextTheme.subhead.decorationColor,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: null,
),
);
}
}

View file

@ -0,0 +1,157 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/keyboard_done_button.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/themes/theme_base.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
import 'package:cake_wallet/generated/i18n.dart';
class IoniaBuyGiftCardPage extends BasePage {
IoniaBuyGiftCardPage()
: _amountFieldFocus = FocusNode(),
_amountController = TextEditingController();
@override
String get title => S.current.enter_amount;
@override
Color get titleColor => Colors.white;
@override
bool get extendBodyBehindAppBar => true;
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
Color get textColor => currentTheme.type == ThemeType.dark ? Colors.white : Color(0xff393939);
final TextEditingController _amountController;
final FocusNode _amountFieldFocus;
@override
Widget body(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
return KeyboardActions(
disableScroll: true,
config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).accentTextTheme.body2.backgroundColor,
nextFocus: false,
actions: [
KeyboardActionsItem(
focusNode: _amountFieldFocus,
toolbarButtons: [(_) => KeyboardDoneButton()],
),
]),
child: Container(
color: Theme.of(context).backgroundColor,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.zero,
content: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 25),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.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(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: 150),
BaseTextFormField(
controller: _amountController,
focusNode: _amountFieldFocus,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))],
hintText: '1000',
placeholderTextStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.headline.color,
fontWeight: FontWeight.w500,
fontSize: 36,
),
borderColor: Theme.of(context).primaryTextTheme.headline.color,
textColor: Colors.white,
textStyle: TextStyle(
color: Colors.white,
fontSize: 36,
),
suffixIcon: SizedBox(
width: _width / 6,
),
prefixIcon: Padding(
padding: EdgeInsets.only(
top: 5.0,
left: _width / 4,
),
child: Text(
'USD: ',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 36,
),
),
),
),
SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).min_amount('5'),
style: TextStyle(
color: Theme.of(context).primaryTextTheme.headline.color,
),
),
Text(
S.of(context).max_amount('20000'),
style: TextStyle(
color: Theme.of(context).primaryTextTheme.headline.color,
),
),
],
),
SizedBox(height: 24),
],
),
),
Padding(
padding: const EdgeInsets.all(24.0),
child: CardItem(
onTap: () {},
title: 'Applebees',
hasDiscount: true,
subTitle: 'subTitle',
logoUrl: '',
),
)
],
),
bottomSection: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton(
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: 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',
color: Theme.of(context).accentTextTheme.display4.backgroundColor,
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: _IoniaDebitCard(
cardInfo: cardState.card,
),
),
bottomSection: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
S.of(context).billing_address_info,
style: textSmall(color: Theme.of(context).textTheme.display1.color),
textAlign: TextAlign.center,
),
),
SizedBox(height: 24),
PrimaryButton(
text: S.of(context).order_physical_card,
onPressed: () {},
color: Color(0xffE9F2FC),
textColor: Theme.of(context).textTheme.display2.color,
),
SizedBox(height: 8),
PrimaryButton(
text: S.of(context).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: S.of(context).get_a,
style: textMedium(color: Theme.of(context).textTheme.display2.color),
children: [
TextSpan(
text: S.of(context).digital_and_physical_card,
style: textMediumBold(color: Theme.of(context).textTheme.display2.color),
),
TextSpan(
text: S.of(context).get_card_note,
)
],
)),
),
],
),
),
bottomSectionPadding: EdgeInsets.symmetric(
horizontal: 16,
vertical: 32,
),
bottomSection: PrimaryButton(
text: S.of(context).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(
S.of(context).how_to_use_card,
style: textLargeSemiBold(
color: Theme.of(context).textTheme.body1.color,
),
),
SizedBox(height: 24),
Align(
alignment: Alignment.bottomLeft,
child: Text(
S.of(context).signup_for_card_accept_terms,
style: textSmallSemiBold(
color: Theme.of(context).textTheme.display2.color,
),
),
),
SizedBox(height: 24),
_TitleSubtitleTile(
title: S.of(context).add_fund_to_card('1000'),
subtitle: S.of(context).use_card_info_two,
),
SizedBox(height: 21),
_TitleSubtitleTile(
title: S.of(context).use_card_info_three,
subtitle: S.of(context).optionally_order_card,
),
SizedBox(height: 35),
PrimaryButton(
onPressed: () => activate
? Navigator.pushNamed(context, Routes.ioniaActivateDebitCardPage)
: Navigator.pop(context),
text: S.of(context).send_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)} ');
}
String get _getLast4 => widget.isCardSample ? '0000' : widget.cardInfo.pan.substring(widget.cardInfo.pan.length - 5);
String get _getSpendLimit => widget.isCardSample ? '10000' : widget.cardInfo.spendLimit.toStringAsFixed(2);
@override
Widget build(BuildContext context) {
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 ? S.of(context).upto(_getSpendLimit) : '\$$_getSpendLimit',
style: textXLargeSemiBold(),
),
SizedBox(height: 16),
Text(
_showDetails ? _formatPan(widget.cardInfo.pan) : '**** **** **** $_getLast4',
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(
S.of(context).expires,
style: textXSmallSemiBold(),
),
SizedBox(height: 4),
Text(
'${widget.cardInfo.expirationMonth ?? S.of(context).mm}/${widget.cardInfo.expirationYear ?? S.of(context).yy}',
style: textMediumSemiBold(),
)
],
),
]
],
),
if (!widget.isCardSample) ...[
SizedBox(height: 8),
Center(
child: InkWell(
onTap: () => _toggleVisibility(),
child: Text(
_showDetails ? S.of(context).hide_details : S.of(context).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

@ -0,0 +1,247 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/ionia/widgets/card_item.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 IoniaManageCardsPage extends BasePage {
IoniaManageCardsPage(this._ioniaViewModel);
final IoniaViewModel _ioniaViewModel;
@override
Color get backgroundLightColor => currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@override
Color get backgroundDarkColor => Colors.transparent;
@override
Color get titleColor => currentTheme.type == ThemeType.bright ? Colors.white : Colors.black;
@override
Widget Function(BuildContext, Widget) get rootWrapper => (BuildContext context, Widget scaffold) => Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).accentColor,
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor,
],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
),
),
child: scaffold,
);
@override
bool get resizeToAvoidBottomInset => false;
@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(
S.of(context).manage_cards,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.display3.backgroundColor,
),
);
}
final ScrollController _scrollController = ScrollController();
@override
Widget trailing(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
_TrailingIcon(
asset: 'assets/images/card.png',
onPressed: () => Navigator.pushNamed(context, Routes.ioniaDebitCardPage),
),
SizedBox(width: 16),
_TrailingIcon(
asset: 'assets/images/profile.png',
onPressed: () {},
),
],
);
}
@override
Widget body(BuildContext context) {
final filterIcon = Image.asset(
'assets/images/filter.png',
color: Theme.of(context).textTheme.caption.decorationColor,
);
return Padding(
padding: const EdgeInsets.all(14.0),
child: Column(
children: [
MarketPlaceItem(
onTap: () {},
title: S.of(context).setup_your_debit_card,
subTitle: S.of(context).no_id_required,
),
SizedBox(height: 48),
Container(
padding: EdgeInsets.only(left: 2, right: 22),
height: 32,
child: Row(
children: [
Expanded(child: _SearchWidget()),
SizedBox(width: 10),
Container(
width: 32,
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
border: Border.all(
color: Colors.white.withOpacity(0.2),
),
borderRadius: BorderRadius.circular(10),
),
child: filterIcon,
)
],
),
),
SizedBox(height: 8),
Expanded(
child: RawScrollbar(
thumbColor: Colors.white.withOpacity(0.15),
radius: Radius.circular(20),
isAlwaysShown: true,
thickness: 2,
controller: _scrollController,
child: ListView.separated(
padding: EdgeInsets.only(left: 2, right: 22),
controller: _scrollController,
itemCount: 20,
separatorBuilder: (_, __) => SizedBox(height: 4),
itemBuilder: (_, index) {
return CardItem(
logoUrl: '',
onTap: () => Navigator.of(context).pushNamed(Routes.ioniaBuyGiftCardPage),
title: 'Amazon',
subTitle: 'Onlin',
hasDiscount: true,
);
},
),
),
),
],
),
);
}
}
class _SearchWidget extends StatelessWidget {
const _SearchWidget({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final searchIcon = Padding(
padding: EdgeInsets.all(8),
child: Image.asset(
'assets/images/search_icon.png',
color: Theme.of(context).textTheme.caption.decorationColor,
),
);
return TextField(
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
filled: true,
contentPadding: EdgeInsets.only(
top: 10,
left: 10,
),
fillColor: Colors.white.withOpacity(0.15),
hintText: 'Search',
hintStyle: TextStyle(
color: Colors.white.withOpacity(0.6),
),
alignLabelWithHint: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
suffixIcon: searchIcon,
border: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white.withOpacity(0.2),
),
borderRadius: BorderRadius.circular(10),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.white.withOpacity(0.2),
),
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white.withOpacity(0.2)),
borderRadius: BorderRadius.circular(10),
)),
);
}
}
class _TrailingIcon extends StatelessWidget {
final String asset;
final VoidCallback onPressed;
const _TrailingIcon({this.asset, this.onPressed});
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.centerRight,
width: 25,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: onPressed,
child: Image.asset(
asset,
color: Theme.of(context).accentTextTheme.display3.backgroundColor,
),
),
);
}
}

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,122 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/discount_badge.dart';
import 'package:flutter/material.dart';
class CardItem extends StatelessWidget {
CardItem({
@required this.onTap,
@required this.title,
@required this.subTitle,
this.logoUrl,
this.hasDiscount = false,
});
final VoidCallback onTap;
final String title;
final String subTitle;
final String logoUrl;
final bool hasDiscount;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Stack(
children: [
Container(
padding: EdgeInsets.all(12),
width: double.infinity,
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.white.withOpacity(0.20),
),
),
child: Row(
children: [
if (logoUrl != null) ...[
ClipOval(
child: Image.network(
logoUrl,
width: 42.0,
height: 42.0,
loadingBuilder: (BuildContext _, Widget child, ImageChunkEvent loadingProgress) {
if (loadingProgress == null) {
return child;
} else {
return _PlaceholderContainer(text: 'Logo');
}
},
errorBuilder: (_, __, ___) => _PlaceholderContainer(text: '!'),
),
),
SizedBox(width: 5),
],
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
color: Palette.stateGray,
fontSize: 24,
fontWeight: FontWeight.w900,
),
),
SizedBox(height: 5),
Text(
subTitle,
style: TextStyle(
color: Palette.niagara ,
fontWeight: FontWeight.w500,
fontFamily: 'Lato'),
)
],
),
],
),
),
if (hasDiscount)
Align(
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: DiscountBadge(),
),
),
],
),
);
}
}
class _PlaceholderContainer extends StatelessWidget {
const _PlaceholderContainer({@required this.text});
final String text;
@override
Widget build(BuildContext context) {
return Container(
height: 42,
width: 42,
child: Center(
child: Text(
text,
style: TextStyle(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.w900,
),
),
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(100),
),
);
}
}

View file

@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
class CardMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
);
}
}

View file

@ -0,0 +1,146 @@
import 'dart:ui';
import 'package:cake_wallet/palette.dart';
import 'package:flutter/material.dart';
class IoniaConfirmModal extends StatelessWidget {
IoniaConfirmModal({
@required this.alertTitle,
@required this.alertContent,
@required this.leftButtonText,
@required this.rightButtonText,
@required this.actionLeftButton,
@required this.actionRightButton,
this.leftActionColor,
this.rightActionColor,
});
final String alertTitle;
final Widget alertContent;
final String leftButtonText;
final String rightButtonText;
final VoidCallback actionLeftButton;
final VoidCallback actionRightButton;
final Color leftActionColor;
final Color rightActionColor;
Widget actionButtons(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
IoniaActionButton(
buttonText: leftButtonText,
action: actionLeftButton,
backgoundColor: leftActionColor,
),
Container(
width: 1,
height: 52,
color: Theme.of(context).dividerColor,
),
IoniaActionButton(
buttonText: rightButtonText,
action: actionRightButton,
backgoundColor: rightActionColor,
),
],
);
}
Widget title(BuildContext context) {
return Text(
alertTitle,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
);
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: Center(
child: GestureDetector(
onTap: () => null,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(30)),
child: Container(
width: 327,
color: Theme.of(context).accentTextTheme.title.decorationColor,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.fromLTRB(24, 20, 24, 0),
child: title(context),
),
Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
),
alertContent,
actionButtons(context),
],
),
),
),
),
),
),
),
);
}
}
class IoniaActionButton extends StatelessWidget {
const IoniaActionButton({
@required this.buttonText,
@required this.action,
this.backgoundColor,
});
final String buttonText;
final VoidCallback action;
final Color backgoundColor;
@override
Widget build(BuildContext context) {
return Flexible(
child: Container(
height: 52,
padding: EdgeInsets.only(left: 6, right: 6),
color: backgoundColor,
child: ButtonTheme(
minWidth: double.infinity,
child: FlatButton(
onPressed: action,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Text(
buttonText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: backgoundColor != null ? Colors.white : Theme.of(context).primaryTextTheme.body1.backgroundColor,
decoration: TextDecoration.none,
),
)),
),
));
}
}

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

@ -10,7 +10,10 @@ class AlertWithTwoActions extends BaseAlertDialog {
@required this.rightButtonText,
@required this.actionLeftButton,
@required this.actionRightButton,
this.alertBarrierDismissible = true
this.alertBarrierDismissible = true,
this.isDividerExist = false,
this.leftActionColor,
this.rightActionColor,
});
final String alertTitle;
@ -20,6 +23,9 @@ class AlertWithTwoActions extends BaseAlertDialog {
final VoidCallback actionLeftButton;
final VoidCallback actionRightButton;
final bool alertBarrierDismissible;
final Color leftActionColor;
final Color rightActionColor;
final bool isDividerExist;
@override
String get titleText => alertTitle;
@ -35,4 +41,10 @@ class AlertWithTwoActions extends BaseAlertDialog {
VoidCallback get actionRight => actionRightButton;
@override
bool get barrierDismissible => alertBarrierDismissible;
}
@override
Color get leftButtonColor => leftActionColor;
@override
Color get rightButtonColor => rightActionColor;
@override
bool get isDividerExists => isDividerExist;
}

View file

@ -46,30 +46,28 @@ class BaseAlertDialog extends StatelessWidget {
children: <Widget>[
Flexible(
child: Container(
height: 52,
padding: EdgeInsets.only(left: 6, right: 6),
color: Theme.of(context).accentTextTheme.body2.decorationColor,
child: ButtonTheme(
minWidth: double.infinity,
child: FlatButton(
onPressed: actionLeft,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Text(
leftActionButtonText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.body2
.backgroundColor,
decoration: TextDecoration.none,
),
)),
),
)
),
height: 52,
padding: EdgeInsets.only(left: 6, right: 6),
color: Theme.of(context).accentTextTheme.body2.decorationColor,
child: ButtonTheme(
minWidth: double.infinity,
child: FlatButton(
onPressed: actionLeft,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Text(
leftActionButtonText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.body2.backgroundColor,
decoration: TextDecoration.none,
),
)),
),
)),
Container(
width: 1,
height: 52,
@ -77,30 +75,28 @@ class BaseAlertDialog extends StatelessWidget {
),
Flexible(
child: Container(
height: 52,
padding: EdgeInsets.only(left: 6, right: 6),
color: Theme.of(context).accentTextTheme.body1.backgroundColor,
child: ButtonTheme(
minWidth: double.infinity,
child: FlatButton(
onPressed: actionRight,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Text(
rightActionButtonText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.body1
.backgroundColor,
decoration: TextDecoration.none,
),
)),
),
)
),
height: 52,
padding: EdgeInsets.only(left: 6, right: 6),
color: Theme.of(context).accentTextTheme.body1.backgroundColor,
child: ButtonTheme(
minWidth: double.infinity,
child: FlatButton(
onPressed: actionRight,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Text(
rightActionButtonText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.body1.backgroundColor,
decoration: TextDecoration.none,
),
)),
),
)),
],
);
}
@ -108,9 +104,7 @@ class BaseAlertDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => barrierDismissible
? Navigator.of(context).pop()
: null,
onTap: () => barrierDismissible ? Navigator.of(context).pop() : null,
child: Container(
color: Colors.transparent,
child: BackdropFilter(
@ -136,14 +130,14 @@ class BaseAlertDialog extends StatelessWidget {
child: title(context),
),
isDividerExists
? Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
)
: Offstage(),
? Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
)
: Offstage(),
Padding(
padding: EdgeInsets.fromLTRB(24, 8, 24, 32),
child: content(context),
@ -166,4 +160,4 @@ class BaseAlertDialog extends StatelessWidget {
),
);
}
}
}

View file

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class DiscountBadge extends StatelessWidget {
const DiscountBadge({
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.centerRight,
children: [
Image.asset('assets/images/badge_discount.png'),
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: Text(
S.of(context).discount('20'),
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.w500,
fontFamily: 'Lato',
),
),
)
],
);
}
}

View file

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
class MarketPlaceItem extends StatelessWidget {
MarketPlaceItem({
@required this.onTap,
@required this.title,
@required this.subTitle,
});
final VoidCallback onTap;
final String title;
final String subTitle;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Stack(
children: [
Container(
padding: EdgeInsets.all(20),
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).textTheme.title.backgroundColor,
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.white.withOpacity(0.20),
),
),
child:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
fontSize: 24,
fontWeight: FontWeight.w900,
),
),
SizedBox(height: 5),
Text(
subTitle,
style: TextStyle(
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
fontWeight: FontWeight.w500,
fontFamily: 'Lato'),
)
],
),
),
],
),
);
}
}

59
lib/typography.dart Normal file
View file

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
const latoFont = "Lato";
TextStyle textXxSmall({Color color}) => _cakeRegular(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);
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);
TextStyle textLargeSemiBold({Color color}) => _cakeSemiBold(24, color);
TextStyle textXLarge({Color color}) => _cakeRegular(32, color);
TextStyle textXLargeSemiBold({Color color}) => _cakeSemiBold(32, color);
TextStyle _cakeRegular(double size, Color color) => _textStyle(
size: size,
fontWeight: FontWeight.normal,
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,
color: color,
);
TextStyle _textStyle({
@required double size,
@required FontWeight fontWeight,
Color color,
}) =>
TextStyle(
fontFamily: latoFont,
fontSize: size,
fontWeight: fontWeight,
color: color ?? Colors.white,
);

View file

@ -0,0 +1,91 @@
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';
class IoniaViewModel = IoniaViewModelBase with _$IoniaViewModel;
abstract class IoniaViewModelBase with Store {
IoniaViewModelBase({this.ioniaService})
: createUserState = IoniaCreateStateSuccess(),
otpState = IoniaOtpSendDisabled(),
cardState = IoniaNoCardState() {
_getCard();
_getAuthStatus().then((value) => isLoggedIn = value);
}
final IoniaService ioniaService;
@observable
IoniaCreateAccountState createUserState;
@observable
IoniaOtpState otpState;
@observable
IoniaCreateCardState createCardState;
@observable
IoniaFetchCardState cardState;
@observable
String email;
@observable
String otp;
@observable
bool isLoggedIn;
@action
Future<void> createUser(String email) async {
createUserState = IoniaCreateStateLoading();
try {
await ioniaService.createUser(email);
createUserState = IoniaCreateStateSuccess();
} on Exception catch (e) {
createUserState = IoniaCreateStateFailure(error: e.toString());
}
}
@action
Future<void> verifyEmail(String code) async {
try {
otpState = IoniaOtpValidating();
await ioniaService.verifyEmail(code);
otpState = IoniaOtpSuccess();
} catch (_) {
otpState = IoniaOtpFailure(error: 'Invalid OTP. Try again');
}
}
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;
} on Exception catch (e) {
createCardState = IoniaCreateCardFailure(error: e.toString());
}
return null;
}
Future<void> _getCard() async {
cardState = IoniaFetchingCard();
try {
final card = await ioniaService.getCard();
cardState = IoniaCardSuccess(card: card);
} catch (_) {
cardState = IoniaFetchCardFailure();
}
}
}

View file

@ -530,5 +530,69 @@
"learn_more" : "Learn More",
"search": "Search",
"new_template" : "New Template",
"electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work"
"electrum_address_disclaimer": "We generate new addresses each time you use one, but previous addresses continue to work",
"market_place": "Market place",
"cake_pay_title": "Gift cards and debit cards",
"cake_pay_subtitle": "Buy gift cards and top up no-KYC debit cards",
"about_cake_pay": "CakePay allows you to easily buy gift cards and load up prepaid debit cards with cryptocurrencies, spendable at millions of merchants in the United States.",
"cake_pay_account_note": "Make an account to see the available cards. Some are even available at a discount!",
"already_have_account": "Already have an account?",
"create_account": "Create Account",
"privacy_policy": "Privacy policy",
"welcome_to_cakepay": "Welcome to CakePay!",
"sign_up": "Sign Up",
"forgot_password": "Forgot Password",
"reset_password": "Reset Password",
"manage_cards": "Manage Cards",
"setup_your_debit_card": "Set up your debit card",
"no_id_required": "No ID required. Top up and spend anywhere",
"how_to_use_card": "How to use this card",
"purchase_gift_card": "Purchase Gift Card",
"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",
"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",
"email_address": "Email Address",
"agree_to": "By creating account you agree to the ",
"and": "and",
"enter_code": "Enter code",
"congratulations": "Congratulations!",
"you_now_have_debit_card": "You now have a debit card",
"min_amount" : "Min: ${value}",
"max_amount" : "Max: ${value}",
"enter_amount": "Enter Amount",
"billing_address_info": "If asked for a billing address, provide your shipping address",
"order_physical_card": "Order Physical Card",
"add_value": "Add value",
"activate": "Activate",
"get_a": "Get a ",
"digital_and_physical_card": " digital and physical prepaid debit card",
"get_card_note": " that you can reload with digital currencies. No additional information needed!",
"signup_for_card_accept_terms": "Sign up for the card and accept the terms.",
"add_fund_to_card": "Add prepaid funds to the cards (up to ${value})",
"use_card_info_two": "Funds are converted to USD when the held in the prepaid account, not in digital currencies.",
"use_card_info_three": "Use the digital card online or with contactless payment methods.",
"optionally_order_card": "Optionally order a physical card.",
"hide_details" : "Hide Details",
"show_details" : "Show Details",
"upto": "up to ${value}",
"discount": "Save ${value}%",
"gift_card_amount": "Gift Card Amount",
"bill_amount": "Bill amount",
"you_pay": "You pay",
"tip": "Tip:",
"custom": "custom",
"by_cake_pay": "by CakePay",
"expires": "Expires",
"mm": "MM",
"yy": "YY"
}