fix issue with custom redeem amount remaining

This commit is contained in:
Godwin Asuquo 2022-12-07 18:12:45 +01:00
parent 8511805ee3
commit 0203f686c5
6 changed files with 156 additions and 122 deletions

View file

@ -760,7 +760,8 @@ Future setup(
return IoniaMoreOptionsPage(giftCard); return IoniaMoreOptionsPage(giftCard);
}); });
getIt.registerFactoryParam<IoniaCustomRedeemViewModel, IoniaGiftCard, void>((IoniaGiftCard giftCard, _) => IoniaCustomRedeemViewModel(giftCard)); getIt.registerFactoryParam<IoniaCustomRedeemViewModel, IoniaGiftCard, void>((IoniaGiftCard giftCard, _)
=> IoniaCustomRedeemViewModel(giftCard: giftCard, ioniaService: getIt.get<IoniaService>()));
getIt.registerFactoryParam<IoniaCustomRedeemPage, List, void>((List args, _){ getIt.registerFactoryParam<IoniaCustomRedeemPage, List, void>((List args, _){
final giftCard = args.first as IoniaGiftCard; final giftCard = args.first as IoniaGiftCard;

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/screens/base_page.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_item.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
@ -18,13 +19,12 @@ class IoniaCustomRedeemPage extends BasePage {
) : _amountFieldFocus = FocusNode(), ) : _amountFieldFocus = FocusNode(),
_amountController = TextEditingController() { _amountController = TextEditingController() {
_amountController.addListener(() { _amountController.addListener(() {
ioniaCustomRedeemViewModel.updateAmount(_amountController.text); ioniaCustomRedeemViewModel.updateAmount(_amountController.text);
}); });
} }
final IoniaCustomRedeemViewModel ioniaCustomRedeemViewModel; final IoniaCustomRedeemViewModel ioniaCustomRedeemViewModel;
@override @override
String get title => S.current.custom_redeem_amount; String get title => S.current.custom_redeem_amount;
@ -50,7 +50,7 @@ class IoniaCustomRedeemPage extends BasePage {
disableScroll: true, disableScroll: true,
config: KeyboardActionsConfig( config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS, keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!, keyboardBarColor: Theme.of(context).accentTextTheme.bodyText1!.backgroundColor!,
nextFocus: false, nextFocus: false,
actions: [ actions: [
KeyboardActionsItem( KeyboardActionsItem(
@ -67,10 +67,11 @@ class IoniaCustomRedeemPage extends BasePage {
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 25), padding: EdgeInsets.symmetric(horizontal: 25),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
gradient: LinearGradient(colors: [ gradient: LinearGradient(colors: [
Theme.of(context).primaryTextTheme!.subtitle1!.color!, Theme.of(context).primaryTextTheme.subtitle1!.color!,
Theme.of(context).primaryTextTheme!.subtitle1!.decorationColor!, Theme.of(context).primaryTextTheme.subtitle1!.decorationColor!,
], begin: Alignment.topLeft, end: Alignment.bottomRight), ], begin: Alignment.topLeft, end: Alignment.bottomRight),
), ),
child: Column( child: Column(
@ -85,11 +86,11 @@ class IoniaCustomRedeemPage extends BasePage {
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))], inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\-|\ ]'))],
hintText: '1000', hintText: '1000',
placeholderTextStyle: TextStyle( placeholderTextStyle: TextStyle(
color: Theme.of(context).primaryTextTheme!.headline5!.color!, color: Theme.of(context).primaryTextTheme.headline5!.color!,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontSize: 36, fontSize: 36,
), ),
borderColor: Theme.of(context).primaryTextTheme!.headline5!.color!, borderColor: Theme.of(context).primaryTextTheme.headline5!.color!,
textColor: Colors.white, textColor: Colors.white,
textStyle: TextStyle( textStyle: TextStyle(
color: Colors.white, color: Colors.white,
@ -114,14 +115,17 @@ class IoniaCustomRedeemPage extends BasePage {
), ),
), ),
SizedBox(height: 8), SizedBox(height: 8),
Observer(builder: (_)=> Observer(
!ioniaCustomRedeemViewModel.disableRedeem ? builder: (_) => !ioniaCustomRedeemViewModel.disableRedeem
Center( ? Center(
child: Text('\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}', child: Text(
style: TextStyle( '\$${giftCard.remainingAmount} - \$${ioniaCustomRedeemViewModel.amount} = \$${ioniaCustomRedeemViewModel.formattedRemaining} ${S.of(context).remaining}',
color: Theme.of(context).primaryTextTheme!.headline5!.color!, style: TextStyle(
),), color: Theme.of(context).primaryTextTheme.headline5!.color!,
) : SizedBox.shrink(), ),
),
)
: SizedBox.shrink(),
), ),
SizedBox(height: 24), SizedBox(height: 24),
], ],
@ -131,11 +135,15 @@ class IoniaCustomRedeemPage extends BasePage {
padding: const EdgeInsets.all(24.0), padding: const EdgeInsets.all(24.0),
child: CardItem( child: CardItem(
title: giftCard.legalName, title: giftCard.legalName,
backgroundColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!.withOpacity(0.1), backgroundColor: Theme.of(context)
.accentTextTheme
.headline1!
.backgroundColor!
.withOpacity(0.1),
discount: giftCard.remainingAmount, discount: giftCard.remainingAmount,
isAmount: true, isAmount: true,
discountBackground: AssetImage('assets/images/red_badge_discount.png'), discountBackground: AssetImage('assets/images/red_badge_discount.png'),
titleColor: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!, titleColor: Theme.of(context).accentTextTheme.headline1!.backgroundColor!,
subtitleColor: Theme.of(context).hintColor, subtitleColor: Theme.of(context).hintColor,
subTitle: S.of(context).online, subTitle: S.of(context).online,
logoUrl: giftCard.logoUrl, logoUrl: giftCard.logoUrl,
@ -145,16 +153,19 @@ class IoniaCustomRedeemPage extends BasePage {
), ),
bottomSection: Column( bottomSection: Column(
children: [ children: [
Padding( Observer(
padding: EdgeInsets.only(bottom: 12), builder: (_) => Padding(
child: PrimaryButton( padding: EdgeInsets.only(bottom: 12),
onPressed: () { child: LoadingPrimaryButton(
Navigator.of(context).pop([ioniaCustomRedeemViewModel.remaining.toString(), ioniaCustomRedeemViewModel.amount.toString()]); isLoading: ioniaCustomRedeemViewModel.redeemState is IsExecutingState,
}, isDisabled: ioniaCustomRedeemViewModel.disableRedeem,
isDisabled: ioniaCustomRedeemViewModel.disableRedeem, text: S.of(context).add_custom_redemption,
text: S.of(context).add_custom_redemption, color: Theme.of(context).accentTextTheme.bodyText1!.color!,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!, textColor: Colors.white,
textColor: Colors.white, onPressed: () => ioniaCustomRedeemViewModel.addCustomRedeem().then((value) {
Navigator.of(context).pop(ioniaCustomRedeemViewModel.remaining.toString());
}),
),
), ),
), ),
SizedBox(height: 30), SizedBox(height: 30),

View file

@ -32,7 +32,7 @@ class IoniaGiftCardDetailPage extends BasePage {
final _backButton = Icon( final _backButton = Icon(
Icons.arrow_back_ios, Icons.arrow_back_ios,
color: Theme.of(context).primaryTextTheme!.headline6!.color!, color: Theme.of(context).primaryTextTheme.headline6!.color!,
size: 16, size: 16,
); );
return Padding( return Padding(
@ -43,7 +43,7 @@ class IoniaGiftCardDetailPage extends BasePage {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: TextButton( child: TextButton(
// FIX-ME: Style // FIX-ME: Style
//highlightColor: Colors.transparent, //highlightColor: Colors.transparent,
//splashColor: Colors.transparent, //splashColor: Colors.transparent,
//padding: EdgeInsets.all(0), //padding: EdgeInsets.all(0),
@ -61,7 +61,8 @@ class IoniaGiftCardDetailPage extends BasePage {
Widget middle(BuildContext context) { Widget middle(BuildContext context) {
return Text( return Text(
viewModel.giftCard.legalName, viewModel.giftCard.legalName,
style: textMediumSemiBold(color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!), style:
textMediumSemiBold(color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!),
); );
} }
@ -102,20 +103,21 @@ class IoniaGiftCardDetailPage extends BasePage {
title: S.of(context).gift_card_number, title: S.of(context).gift_card_number,
subTitle: viewModel.giftCard.cardNumber, subTitle: viewModel.giftCard.cardNumber,
), ),
if (viewModel.giftCard.cardPin?.isNotEmpty ?? false) if (viewModel.giftCard.cardPin.isNotEmpty) ...[
...[Divider(height: 30), Divider(height: 30),
buildIoniaTile( buildIoniaTile(
context, context,
title: S.of(context).pin_number, title: S.of(context).pin_number,
subTitle: viewModel.giftCard.cardPin, subTitle: viewModel.giftCard.cardPin,
)], )
],
Divider(height: 30), Divider(height: 30),
Observer(builder: (_) => Observer(
buildIoniaTile( builder: (_) => buildIoniaTile(
context, context,
title: S.of(context).amount, title: S.of(context).amount,
subTitle: viewModel.remainingAmount.toStringAsFixed(2) ?? '0.00', subTitle: viewModel.remainingAmount.toStringAsFixed(2),
)), )),
Divider(height: 50), Divider(height: 50),
TextIconButton( TextIconButton(
label: S.of(context).how_to_use_card, label: S.of(context).how_to_use_card,
@ -131,24 +133,23 @@ class IoniaGiftCardDetailPage extends BasePage {
return Column( return Column(
children: [ children: [
PrimaryButton( PrimaryButton(
onPressed: () async { onPressed: () async {
final amount = await Navigator.of(context) await Navigator.of(context).pushNamed(
.pushNamed(Routes.ioniaMoreOptionsPage, arguments: [viewModel.giftCard]) as List<String>?; Routes.ioniaMoreOptionsPage,
if (amount != null) { arguments: [viewModel.giftCard]) as String?;
viewModel.updateRemaining( balance: double.parse(amount.first), customAmount: double.parse(amount.last)); viewModel.refeshCard();
} },
}, text: S.of(context).more_options,
text: S.of(context).more_options, color: Theme.of(context).accentTextTheme.caption!.color!,
color: Theme.of(context).accentTextTheme.caption!.color!, textColor: Theme.of(context).primaryTextTheme.headline6!.color!,
textColor: Theme.of(context).primaryTextTheme.headline6!.color!,
), ),
SizedBox(height: 12), SizedBox(height: 12),
LoadingPrimaryButton( LoadingPrimaryButton(
isLoading: viewModel.redeemState is IsExecutingState, isLoading: viewModel.redeemState is IsExecutingState,
onPressed: () => viewModel.redeem().then( onPressed: () => viewModel.redeem().then(
(_) { (_) {
Navigator.of(context) Navigator.of(context).pushNamedAndRemoveUntil(
.pushNamedAndRemoveUntil(Routes.ioniaManageCardsPage, (route) => route.isFirst); Routes.ioniaManageCardsPage, (route) => route.isFirst);
}, },
), ),
text: S.of(context).mark_as_redeemed, text: S.of(context).mark_as_redeemed,
@ -168,12 +169,11 @@ class IoniaGiftCardDetailPage extends BasePage {
Widget buildIoniaTile(BuildContext context, {required String title, required String subTitle}) { Widget buildIoniaTile(BuildContext context, {required String title, required String subTitle}) {
return IoniaTile( return IoniaTile(
title: title, title: title,
subTitle: subTitle, subTitle: subTitle,
onTap: () { onTap: () {
Clipboard.setData(ClipboardData(text: subTitle)); Clipboard.setData(ClipboardData(text: subTitle));
showBar<void>(context, showBar<void>(context, S.of(context).transaction_details_copied(title));
S.of(context).transaction_details_copied(title));
}); });
} }
@ -184,10 +184,10 @@ class IoniaGiftCardDetailPage extends BasePage {
showPopUp<void>( showPopUp<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return IoniaAlertModal( return IoniaAlertModal(
title: S.of(context).how_to_use_card, title: S.of(context).how_to_use_card,
content: Column( content: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: viewModel.giftCard.instructions children: viewModel.giftCard.instructions
.map((instruction) { .map((instruction) {
return [ return [
@ -196,13 +196,13 @@ class IoniaGiftCardDetailPage extends BasePage {
child: Text( child: Text(
instruction.header, instruction.header,
style: textLargeSemiBold( style: textLargeSemiBold(
color: Theme.of(context).textTheme!.headline3!.color!, color: Theme.of(context).textTheme.headline3!.color!,
), ),
)), )),
Text( Text(
instruction.body, instruction.body,
style: textMedium( style: textMedium(
color: Theme.of(context).textTheme!.headline3!.color!, color: Theme.of(context).textTheme.headline3!.color!,
), ),
) )
]; ];
@ -210,7 +210,7 @@ class IoniaGiftCardDetailPage extends BasePage {
.expand((e) => e) .expand((e) => e)
.toList()), .toList()),
actionTitle: S.of(context).send_got_it, actionTitle: S.of(context).send_got_it,
); );
}); });
} }
} }

View file

@ -5,7 +5,6 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/typography.dart'; import 'package:cake_wallet/typography.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class IoniaMoreOptionsPage extends BasePage { class IoniaMoreOptionsPage extends BasePage {
IoniaMoreOptionsPage(this.giftCard); IoniaMoreOptionsPage(this.giftCard);
@ -16,7 +15,7 @@ class IoniaMoreOptionsPage extends BasePage {
return Text( return Text(
S.current.more_options, S.current.more_options,
style: textMediumSemiBold( style: textMediumSemiBold(
color: Theme.of(context).accentTextTheme!.headline1!.backgroundColor!, color: Theme.of(context).accentTextTheme.headline1!.backgroundColor!,
), ),
); );
} }
@ -27,40 +26,46 @@ class IoniaMoreOptionsPage extends BasePage {
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
SizedBox(height: 10,), SizedBox(
Center(child: Text(S.of(context).choose_from_available_options, style: textMedium( height: 10,
color: Theme.of(context).primaryTextTheme!.headline6!.color!, ),
),)), Center(
SizedBox(height: 40,), child: Text(
InkWell( S.of(context).choose_from_available_options,
onTap: () async { style: textMedium(
final amounts = await Navigator.of(context).pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as List<String>; color: Theme.of(context).primaryTextTheme.headline6!.color!,
if(amounts.first.isNotEmpty){ ),
Navigator.pop(context, amounts); )),
} SizedBox(
}, height: 40,
child: _GradiantContainer( ),
content: Padding( InkWell(
padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50), onTap: () async {
child: Text( final amount = await Navigator.of(context)
S.of(context).custom_redeem_amount, .pushNamed(Routes.ioniaCustomRedeemPage, arguments: [giftCard]) as String?;
style: textXLargeSemiBold(), if (amount != null && amount.isNotEmpty) {
), Navigator.pop(context);
), }
), },
) child: _GradiantContainer(
], content: Padding(
), padding: const EdgeInsets.only(top: 24, left: 20, right: 24, bottom: 50),
child: Text(
S.of(context).custom_redeem_amount,
style: textXLargeSemiBold(),
),
),
),
)
],
),
); );
} }
} }
class _GradiantContainer extends StatelessWidget { class _GradiantContainer extends StatelessWidget {
const _GradiantContainer({ const _GradiantContainer({Key? key, required this.content}) : super(key: key);
Key? key,
required this.content
}) : super(key: key);
final Widget content; final Widget content;

View file

@ -1,29 +1,51 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/ionia/ionia_gift_card.dart'; import 'package:cake_wallet/ionia/ionia_gift_card.dart';
import 'package:cake_wallet/ionia/ionia_service.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
part 'ionia_custom_redeem_view_model.g.dart'; part 'ionia_custom_redeem_view_model.g.dart';
class IoniaCustomRedeemViewModel = IoniaCustomRedeemViewModelBase with _$IoniaCustomRedeemViewModel; class IoniaCustomRedeemViewModel = IoniaCustomRedeemViewModelBase with _$IoniaCustomRedeemViewModel;
abstract class IoniaCustomRedeemViewModelBase with Store { abstract class IoniaCustomRedeemViewModelBase with Store {
IoniaCustomRedeemViewModelBase(this.giftCard) IoniaCustomRedeemViewModelBase({
: amount = 0; required this.giftCard,
required this.ioniaService,
}) : amount = 0,
redeemState = InitialExecutionState();
final IoniaGiftCard giftCard; final IoniaGiftCard giftCard;
final IoniaService ioniaService;
@observable
ExecutionState redeemState;
@observable @observable
double amount; double amount;
@computed @computed
double get remaining => amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0; double get remaining =>
amount <= giftCard.remainingAmount ? giftCard.remainingAmount - amount : 0;
@computed @computed
String get formattedRemaining => remaining.toStringAsFixed(2); String get formattedRemaining => remaining.toStringAsFixed(2);
@computed @computed
bool get disableRedeem => amount > giftCard.remainingAmount; bool get disableRedeem => amount > giftCard.remainingAmount;
@action @action
void updateAmount(String text){ void updateAmount(String text) {
amount = text.isEmpty ? 0 : (double.parse(text.replaceAll(',', '.')) ?? 0); amount = text.isEmpty ? 0 : (double.parse(text.replaceAll(',', '.')));
} }
@action
Future<void> addCustomRedeem() async {
try {
redeemState = IsExecutingState();
await ioniaService.redeem(giftCardId: giftCard.id, amount: amount);
redeemState = ExecutedSuccessfullyState();
} catch (e) {
redeemState = FailureState(e.toString());
}
}
} }

View file

@ -6,17 +6,14 @@ import 'package:device_display_brightness/device_display_brightness.dart';
part 'ionia_gift_card_details_view_model.g.dart'; part 'ionia_gift_card_details_view_model.g.dart';
class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase with _$IoniaGiftCardDetailsViewModel; class IoniaGiftCardDetailsViewModel = IoniaGiftCardDetailsViewModelBase
with _$IoniaGiftCardDetailsViewModel;
abstract class IoniaGiftCardDetailsViewModelBase with Store { abstract class IoniaGiftCardDetailsViewModelBase with Store {
IoniaGiftCardDetailsViewModelBase({required this.ioniaService, required this.giftCard})
IoniaGiftCardDetailsViewModelBase({ : redeemState = InitialExecutionState(),
required this.ioniaService, remainingAmount = giftCard.remainingAmount,
required this.giftCard}) brightness = 0;
: redeemState = InitialExecutionState(),
remainingAmount = giftCard.remainingAmount,
adjustedAmount = 0,
brightness = 0;
final IoniaService ioniaService; final IoniaService ioniaService;
@ -28,8 +25,6 @@ abstract class IoniaGiftCardDetailsViewModelBase with Store {
@observable @observable
double remainingAmount; double remainingAmount;
double adjustedAmount;
@observable @observable
ExecutionState redeemState; ExecutionState redeemState;
@ -38,18 +33,18 @@ abstract class IoniaGiftCardDetailsViewModelBase with Store {
giftCard.remainingAmount = remainingAmount; giftCard.remainingAmount = remainingAmount;
try { try {
redeemState = IsExecutingState(); redeemState = IsExecutingState();
await ioniaService.redeem(giftCardId: giftCard.id, amount : adjustedAmount > 0 ? adjustedAmount : giftCard.remainingAmount); await ioniaService.redeem(giftCardId: giftCard.id, amount: giftCard.remainingAmount);
giftCard = await ioniaService.getGiftCard(id: giftCard.id); giftCard = await ioniaService.getGiftCard(id: giftCard.id);
redeemState = ExecutedSuccessfullyState(); redeemState = ExecutedSuccessfullyState();
} catch(e) { } catch (e) {
redeemState = FailureState(e.toString()); redeemState = FailureState(e.toString());
} }
} }
@action @action
void updateRemaining({required double balance, required double customAmount}) { Future<void> refeshCard() async {
remainingAmount = balance; giftCard = await ioniaService.getGiftCard(id: giftCard.id);
adjustedAmount = customAmount; remainingAmount = giftCard.remainingAmount;
} }
void increaseBrightness() async { void increaseBrightness() async {