CW-102 fix logic for ionia issues (#403)
BIN
assets/images/airplane.png
Normal file
After Width: | Height: | Size: 960 B |
BIN
assets/images/category.png
Normal file
After Width: | Height: | Size: 454 B |
BIN
assets/images/copy.png
Normal file
After Width: | Height: | Size: 556 B |
BIN
assets/images/delivery.png
Normal file
After Width: | Height: | Size: 728 B |
BIN
assets/images/food.png
Normal file
After Width: | Height: | Size: 710 B |
BIN
assets/images/gaming.png
Normal file
After Width: | Height: | Size: 705 B |
BIN
assets/images/global.png
Normal file
After Width: | Height: | Size: 997 B |
BIN
assets/images/tshirt.png
Normal file
After Width: | Height: | Size: 583 B |
|
@ -1,3 +1,6 @@
|
|||
import 'package:cake_wallet/anypay/any_pay_chain.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cw_core/monero_amount_format.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/anypay/any_pay_payment_instruction.dart';
|
||||
|
||||
|
@ -35,4 +38,27 @@ class AnyPayPayment {
|
|||
final String chain;
|
||||
final String network;
|
||||
final List<AnyPayPaymentInstruction> instructions;
|
||||
|
||||
String get totalAmount {
|
||||
final total = instructions
|
||||
.fold<int>(0, (int acc, instruction) => acc + instruction.outputs
|
||||
.fold<int>(0, (int outAcc, out) => outAcc + out.amount));
|
||||
switch (chain) {
|
||||
case AnyPayChain.xmr:
|
||||
return moneroAmountToString(amount: total);
|
||||
case AnyPayChain.btc:
|
||||
return bitcoinAmountToString(amount: total);
|
||||
case AnyPayChain.ltc:
|
||||
return bitcoinAmountToString(amount: total);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> get outAddresses {
|
||||
return instructions
|
||||
.map((instuction) => instuction.outputs.map((out) => out.address))
|
||||
.expand((e) => e)
|
||||
.toList();
|
||||
}
|
||||
}
|
80
lib/di.dart
|
@ -1,6 +1,10 @@
|
|||
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_anypay.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_category.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_filter_view_model.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_service.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_api.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
||||
|
@ -8,9 +12,14 @@ 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/cards/ionia_account_cards_page.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_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:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_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';
|
||||
|
@ -107,6 +116,7 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
|||
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
|
@ -263,7 +273,6 @@ Future setup(
|
|||
fiatConvertationStore: getIt.get<FiatConversionStore>()));
|
||||
|
||||
getIt.registerFactory(() => DashboardViewModel(
|
||||
|
||||
balanceViewModel: getIt.get<BalanceViewModel>(),
|
||||
appStore: getIt.get<AppStore>(),
|
||||
tradesStore: getIt.get<TradesStore>(),
|
||||
|
@ -564,10 +573,6 @@ Future setup(
|
|||
|
||||
getIt.registerFactory(() => BackupPage(getIt.get<BackupViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => EditBackupPasswordViewModel(
|
||||
getIt.get<FlutterSecureStorage>(), getIt.get<SecretStore>())
|
||||
..init());
|
||||
|
||||
getIt.registerFactory(
|
||||
() => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>()));
|
||||
|
||||
|
@ -600,10 +605,7 @@ Future setup(
|
|||
final url = args.first as String;
|
||||
final buyViewModel = args[1] as BuyViewModel;
|
||||
|
||||
return BuyWebViewPage(
|
||||
buyViewModel: buyViewModel,
|
||||
ordersStore: getIt.get<OrdersStore>(),
|
||||
url: url);
|
||||
return BuyWebViewPage(buyViewModel: buyViewModel, ordersStore: getIt.get<OrdersStore>(), url: url);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<OrderDetailsViewModel, Order, void>((order, _) {
|
||||
|
@ -656,41 +658,65 @@ Future setup(
|
|||
|
||||
getIt.registerFactory<IoniaService>(
|
||||
() => IoniaService(getIt.get<FlutterSecureStorage>(), getIt.get<IoniaApi>()));
|
||||
|
||||
getIt.registerFactory<IoniaAnyPay>(
|
||||
() => IoniaAnyPay(
|
||||
getIt.get<IoniaService>(),
|
||||
getIt.get<AnyPayApi>(),
|
||||
getIt.get<AppStore>().wallet));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => IoniaViewModel(ioniaService: getIt.get<IoniaService>()));
|
||||
getIt.registerFactory<IoniaFilterViewModel>(() => IoniaFilterViewModel());
|
||||
|
||||
getIt.registerFactory(() => IoniaCreateAccountPage(getIt.get<IoniaViewModel>()));
|
||||
getIt.registerFactory(() => IoniaGiftCardsListViewModel(ioniaService: getIt.get<IoniaService>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaLoginPage(getIt.get<IoniaViewModel>()));
|
||||
getIt.registerFactory(() => IoniaAuthViewModel(ioniaService: getIt.get<IoniaService>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaMerchPurchaseViewModel(getIt.get<IoniaAnyPay>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaAccountViewModel(ioniaService: getIt.get<IoniaService>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaCreateAccountPage(getIt.get<IoniaAuthViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaLoginPage(getIt.get<IoniaAuthViewModel>()));
|
||||
|
||||
getIt.registerFactoryParam<IoniaVerifyIoniaOtp, List, void>((List args, _) {
|
||||
final email = args.first as String;
|
||||
final ioniaViewModel = args[1] as IoniaViewModel;
|
||||
final ioniaAuthViewModel = args[1] as IoniaAuthViewModel;
|
||||
|
||||
return IoniaVerifyIoniaOtp(ioniaViewModel, email);
|
||||
return IoniaVerifyIoniaOtp(ioniaAuthViewModel, email);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => IoniaWelcomePage(getIt.get<IoniaViewModel>()));
|
||||
getIt.registerFactory(() => IoniaWelcomePage(getIt.get<IoniaGiftCardsListViewModel>()));
|
||||
|
||||
getIt.registerFactoryParam<IoniaBuyGiftCardPage, List, void>((List args, _) {
|
||||
final merchant = args.first as IoniaMerchant;
|
||||
|
||||
return IoniaBuyGiftCardPage(merchant);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<IoniaBuyGiftCardDetailPage, List, void>((List args, _) {
|
||||
final merchant = args.first as IoniaMerchant;
|
||||
|
||||
return IoniaBuyGiftCardDetailPage(merchant);
|
||||
return IoniaBuyGiftCardPage(getIt.get<IoniaMerchPurchaseViewModel>(), merchant);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => IoniaManageCardsPage(getIt.get<IoniaViewModel>()));
|
||||
getIt.registerFactoryParam<IoniaBuyGiftCardDetailPage, List, void>((List args, _) {
|
||||
final amount = args.first as String;
|
||||
final merchant = args.last as IoniaMerchant;
|
||||
|
||||
getIt.registerFactory(() => IoniaDebitCardPage(getIt.get<IoniaViewModel>()));
|
||||
return IoniaBuyGiftCardDetailPage(amount, getIt.get<IoniaMerchPurchaseViewModel>(), merchant);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => IoniaActivateDebitCardPage(getIt.get<IoniaViewModel>()));
|
||||
getIt.registerFactoryParam<IoniaCustomTipPage, List, void>((List args, _) {
|
||||
final amount = args.first as String;
|
||||
final merchant = args.last as IoniaMerchant;
|
||||
|
||||
return IoniaCustomTipPage(getIt.get<IoniaMerchPurchaseViewModel>(), amount, merchant);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => IoniaManageCardsPage(getIt.get<IoniaGiftCardsListViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaDebitCardPage(getIt.get<IoniaGiftCardsListViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaActivateDebitCardPage(getIt.get<IoniaGiftCardsListViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaAccountPage(getIt.get<IoniaAccountViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => IoniaAccountCardsPage(getIt.get<IoniaAccountViewModel>()));
|
||||
|
||||
_isSetupFinished = true;
|
||||
}
|
||||
|
|
|
@ -23,12 +23,11 @@ class IoniaAnyPay {
|
|||
|
||||
Future<AnyPayPayment> purchase({
|
||||
@required String merchId,
|
||||
@required double amount,
|
||||
@required String currency}) async {
|
||||
@required double amount}) async {
|
||||
final invoice = await ioniaService.purchaseGiftCard(
|
||||
merchId: merchId,
|
||||
amount: amount,
|
||||
currency: currency);
|
||||
currency: wallet.currency.title.toUpperCase());
|
||||
return anyPayApi.paymentRequest(invoice.uri);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
class IoniaCategory {
|
||||
const IoniaCategory(this.title, this.ids);
|
||||
const IoniaCategory({this.index, this.title, this.ids, this.iconPath});
|
||||
|
||||
static const allCategories = <IoniaCategory>[
|
||||
apparel,
|
||||
onlineOnly,
|
||||
food,
|
||||
entertainment,
|
||||
delivery,
|
||||
travel];
|
||||
static const apparel = IoniaCategory('Apparel', [1]);
|
||||
static const onlineOnly = IoniaCategory('Online Only', [13, 43]);
|
||||
static const food = IoniaCategory('Food', [4]);
|
||||
static const entertainment = IoniaCategory('Entertainment', [5]);
|
||||
static const delivery = IoniaCategory('Delivery', [114, 109]);
|
||||
static const travel = IoniaCategory('Travel', [12]);
|
||||
static const allCategories = <IoniaCategory>[all, apparel, onlineOnly, food, entertainment, delivery, travel];
|
||||
static const all = IoniaCategory(index: 0, title: 'All', ids: [], iconPath: 'assets/images/category.png');
|
||||
static const apparel = IoniaCategory(index: 1, title: 'Apparel', ids: [1], iconPath: 'assets/images/tshirt.png');
|
||||
static const onlineOnly = IoniaCategory(index: 2, title: 'Online Only', ids: [13, 43], iconPath: 'assets/images/global.png');
|
||||
static const food = IoniaCategory(index: 3, title: 'Food', ids: [4], iconPath: 'assets/images/food.png');
|
||||
static const entertainment = IoniaCategory(index: 4, title: 'Entertainment', ids: [5], iconPath: 'assets/images/gaming.png');
|
||||
static const delivery = IoniaCategory(index: 5, title: 'Delivery', ids: [114, 109], iconPath: 'assets/images/delivery.png');
|
||||
static const travel = IoniaCategory(index: 6, title: 'Travel', ids: [12], iconPath: 'assets/images/airplane.png');
|
||||
|
||||
final String title;
|
||||
final List<int> ids;
|
||||
|
||||
final int index;
|
||||
final String title;
|
||||
final List<int> ids;
|
||||
final String iconPath;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class IoniaMerchant {
|
||||
class IoniaMerchant {
|
||||
IoniaMerchant({
|
||||
@required this.id,
|
||||
@required this.legalName,
|
||||
|
@ -114,57 +113,49 @@ class IoniaMerchant {
|
|||
isPayLater: element["IsPayLater"] as bool);
|
||||
}
|
||||
|
||||
final int id;
|
||||
final String legalName;
|
||||
final String systemName;
|
||||
final String description;
|
||||
final String website;
|
||||
final String termsAndConditions;
|
||||
final String logoUrl;
|
||||
final String cardImageUrl;
|
||||
final String cardholderAgreement;
|
||||
final double purchaseFee;
|
||||
final double revenueShare;
|
||||
final double marketingFee;
|
||||
final double minimumDiscount;
|
||||
final double level1;
|
||||
final double level2;
|
||||
final double level3;
|
||||
final double level4;
|
||||
final double level5;
|
||||
final double level6;
|
||||
final double level7;
|
||||
final bool isActive;
|
||||
final bool isDeleted;
|
||||
final bool isOnline;
|
||||
final bool isPhysical;
|
||||
final bool isVariablePurchase;
|
||||
final double minimumCardPurchase;
|
||||
final double maximumCardPurchase;
|
||||
final bool acceptsTips;
|
||||
final String createdDateFormatted;
|
||||
final int createdBy;
|
||||
final bool isRegional;
|
||||
final String modifiedDateFormatted;
|
||||
final int modifiedBy;
|
||||
final String usageInstructions;
|
||||
final String usageInstructionsBak;
|
||||
final int paymentGatewayId;
|
||||
final int giftCardGatewayId;
|
||||
final bool isHtmlDescription;
|
||||
final String purchaseInstructions;
|
||||
final String balanceInstructions;
|
||||
final double amountPerCard;
|
||||
final String processingMessage;
|
||||
final bool hasBarcode;
|
||||
final bool hasInventory;
|
||||
final bool isVoidable;
|
||||
final String receiptMessage;
|
||||
final String cssBorderCode;
|
||||
final String paymentInstructions;
|
||||
final String alderSku;
|
||||
final String ngcSku;
|
||||
final String acceptedCurrency;
|
||||
final String deepLink;
|
||||
final bool isPayLater;
|
||||
final int id; final String legalName; final String systemName; final String description; final String website; final String termsAndConditions; final String logoUrl; final String cardImageUrl; final String cardholderAgreement; final double purchaseFee;
|
||||
final double revenueShare;
|
||||
final double marketingFee;
|
||||
final double minimumDiscount;
|
||||
final double level1;
|
||||
final double level2;
|
||||
final double level3;
|
||||
final double level4;
|
||||
final double level5;
|
||||
final double level6;
|
||||
final double level7;
|
||||
final bool isActive;
|
||||
final bool isDeleted;
|
||||
final bool isOnline;
|
||||
final bool isPhysical;
|
||||
final bool isVariablePurchase;
|
||||
final double minimumCardPurchase;
|
||||
final double maximumCardPurchase;
|
||||
final bool acceptsTips;
|
||||
final String createdDateFormatted;
|
||||
final int createdBy;
|
||||
final bool isRegional;
|
||||
final String modifiedDateFormatted;
|
||||
final int modifiedBy;
|
||||
final String usageInstructions;
|
||||
final String usageInstructionsBak;
|
||||
final int paymentGatewayId;
|
||||
final int giftCardGatewayId;
|
||||
final bool isHtmlDescription;
|
||||
final String purchaseInstructions;
|
||||
final String balanceInstructions;
|
||||
final double amountPerCard;
|
||||
final String processingMessage;
|
||||
final bool hasBarcode;
|
||||
final bool hasInventory;
|
||||
final bool isVoidable;
|
||||
final String receiptMessage;
|
||||
final String cssBorderCode;
|
||||
final String paymentInstructions;
|
||||
final String alderSku;
|
||||
final String ngcSku;
|
||||
final String acceptedCurrency;
|
||||
final String deepLink;
|
||||
final bool isPayLater;
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:cake_wallet/ionia/ionia_category.dart';
|
|||
class IoniaService {
|
||||
IoniaService(this.secureStorage, this.ioniaApi);
|
||||
|
||||
static const ioniaEmailStorageKey = 'ionia_email';
|
||||
static const ioniaUsernameStorageKey = 'ionia_username';
|
||||
static const ioniaPasswordStorageKey = 'ionia_password';
|
||||
|
||||
|
@ -22,6 +23,7 @@ class IoniaService {
|
|||
|
||||
Future<void> createUser(String email) async {
|
||||
final username = await ioniaApi.createUser(email, clientId: clientId);
|
||||
await secureStorage.write(key: ioniaEmailStorageKey, value: email);
|
||||
await secureStorage.write(key: ioniaUsernameStorageKey, value: username);
|
||||
}
|
||||
|
||||
|
@ -33,6 +35,10 @@ class IoniaService {
|
|||
await secureStorage.write(key: ioniaPasswordStorageKey, value: credentials.password);
|
||||
}
|
||||
|
||||
Future<String> getUserEmail() async {
|
||||
return secureStorage.read(key: ioniaEmailStorageKey);
|
||||
}
|
||||
|
||||
// Check is user logined
|
||||
|
||||
Future<bool> isLogined() async {
|
||||
|
|
12
lib/ionia/ionia_tip.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
class IoniaTip {
|
||||
const IoniaTip({this.originalAmount, this.percentage});
|
||||
final double originalAmount;
|
||||
final double percentage;
|
||||
double get additionalAmount => originalAmount * percentage / 100;
|
||||
|
||||
static const tipList = [
|
||||
IoniaTip(originalAmount: 0, percentage: 0),
|
||||
IoniaTip(originalAmount: 10, percentage: 10),
|
||||
IoniaTip(originalAmount: 20, percentage: 20)
|
||||
];
|
||||
}
|
|
@ -2,6 +2,8 @@ import 'dart:async';
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/entities/language_service.dart';
|
||||
import 'package:cake_wallet/buy/order.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_category.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -174,7 +176,8 @@ Future<void> initialSetup(
|
|||
exchangeTemplates: exchangeTemplates,
|
||||
transactionDescriptionBox: transactionDescriptions,
|
||||
ordersSource: ordersSource,
|
||||
unspentCoinsInfoSource: unspentCoinsInfoSource);
|
||||
unspentCoinsInfoSource: unspentCoinsInfoSource,
|
||||
);
|
||||
await bootstrap(navigatorKey);
|
||||
monero?.onStartup();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@ 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/ionia/cards/ionia_account_cards_page.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_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';
|
||||
|
@ -433,6 +436,16 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
case Routes.ioniaActivateDebitCardPage:
|
||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaActivateDebitCardPage>());
|
||||
|
||||
case Routes.ioniaAccountPage:
|
||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaAccountPage>());
|
||||
|
||||
case Routes.ioniaAccountCardsPage:
|
||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaAccountCardsPage>());
|
||||
|
||||
case Routes.ioniaCustomTipPage:
|
||||
final args = settings.arguments as List;
|
||||
return CupertinoPageRoute<void>(builder: (_) =>getIt.get<IoniaCustomTipPage>(param1: args));
|
||||
|
||||
default:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => Scaffold(
|
||||
|
|
|
@ -69,5 +69,8 @@ class Routes {
|
|||
static const ioniaVerifyIoniaOtpPage = '/cake_pay_verify_otp_page';
|
||||
static const ioniaDebitCardPage = '/debit_card_page';
|
||||
static const ioniaActivateDebitCardPage = '/activate_debit_card_page';
|
||||
static const ioniaAccountPage = 'ionia_account_page';
|
||||
static const ioniaAccountCardsPage = 'ionia_account_cards_page';
|
||||
static const ioniaCustomTipPage = 'ionia_custom_tip_page';
|
||||
|
||||
}
|
||||
|
|
|
@ -8,22 +8,22 @@ 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:cake_wallet/view_model/ionia/ionia_auth_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)
|
||||
IoniaCreateAccountPage(this._authViewModel)
|
||||
: _emailFocus = FocusNode(),
|
||||
_emailController = TextEditingController(),
|
||||
_formKey = GlobalKey<FormState>() {
|
||||
_emailController.text = _ioniaViewModel.email;
|
||||
_emailController.addListener(() => _ioniaViewModel.email = _emailController.text);
|
||||
_emailController.text = _authViewModel.email;
|
||||
_emailController.addListener(() => _authViewModel.email = _emailController.text);
|
||||
}
|
||||
|
||||
final IoniaViewModel _ioniaViewModel;
|
||||
final IoniaAuthViewModel _authViewModel;
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
|
||||
|
@ -42,12 +42,12 @@ class IoniaCreateAccountPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
reaction((_) => _ioniaViewModel.createUserState, (IoniaCreateAccountState state) {
|
||||
reaction((_) => _authViewModel.createUserState, (IoniaCreateAccountState state) {
|
||||
if (state is IoniaCreateStateFailure) {
|
||||
_onCreateUserFailure(context, state.error);
|
||||
}
|
||||
if (state is IoniaCreateStateSuccess) {
|
||||
_onCreateSuccessful(context, _ioniaViewModel);
|
||||
_onCreateSuccessful(context, _authViewModel);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -59,6 +59,7 @@ class IoniaCreateAccountPage extends BasePage {
|
|||
hintText: S.of(context).email_address,
|
||||
focusNode: _emailFocus,
|
||||
validator: EmailValidator(),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
controller: _emailController,
|
||||
),
|
||||
),
|
||||
|
@ -75,9 +76,9 @@ class IoniaCreateAccountPage extends BasePage {
|
|||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
await _ioniaViewModel.createUser(_emailController.text);
|
||||
await _authViewModel.createUser(_emailController.text);
|
||||
},
|
||||
isLoading: _ioniaViewModel.createUserState is IoniaCreateStateLoading,
|
||||
isLoading: _authViewModel.createUserState is IoniaCreateStateLoading,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
|
@ -133,9 +134,9 @@ class IoniaCreateAccountPage extends BasePage {
|
|||
});
|
||||
}
|
||||
|
||||
void _onCreateSuccessful(BuildContext context, IoniaViewModel ioniaViewModel) => Navigator.pushNamed(
|
||||
void _onCreateSuccessful(BuildContext context, IoniaAuthViewModel authViewModel) => Navigator.pushNamed(
|
||||
context,
|
||||
Routes.ioniaVerifyIoniaOtpPage,
|
||||
arguments: [ioniaViewModel.email, ioniaViewModel],
|
||||
arguments: [authViewModel.email, authViewModel],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,23 +8,23 @@ 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:cake_wallet/view_model/ionia/ionia_auth_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)
|
||||
IoniaLoginPage(this._authViewModel)
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_emailController = TextEditingController() {
|
||||
_emailController.text = _ioniaViewModel.email;
|
||||
_emailController.addListener(() => _ioniaViewModel.email = _emailController.text);
|
||||
_emailController.text = _authViewModel.email;
|
||||
_emailController.addListener(() => _authViewModel.email = _emailController.text);
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
|
||||
final IoniaViewModel _ioniaViewModel;
|
||||
final IoniaAuthViewModel _authViewModel;
|
||||
|
||||
@override
|
||||
Color get titleColor => Colors.black;
|
||||
|
@ -43,12 +43,12 @@ class IoniaLoginPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
reaction((_) => _ioniaViewModel.createUserState, (IoniaCreateAccountState state) {
|
||||
reaction((_) => _authViewModel.createUserState, (IoniaCreateAccountState state) {
|
||||
if (state is IoniaCreateStateFailure) {
|
||||
_onLoginUserFailure(context, state.error);
|
||||
}
|
||||
if (state is IoniaCreateStateSuccess) {
|
||||
_onLoginSuccessful(context, _ioniaViewModel);
|
||||
_onLoginSuccessful(context, _authViewModel);
|
||||
}
|
||||
});
|
||||
return ScrollableWithBottomSection(
|
||||
|
@ -57,6 +57,7 @@ class IoniaLoginPage extends BasePage {
|
|||
key: _formKey,
|
||||
child: BaseTextFormField(
|
||||
hintText: S.of(context).email_address,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
validator: EmailValidator(),
|
||||
controller: _emailController,
|
||||
),
|
||||
|
@ -74,9 +75,9 @@ class IoniaLoginPage extends BasePage {
|
|||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
await _ioniaViewModel.createUser(_emailController.text);
|
||||
await _authViewModel.createUser(_emailController.text);
|
||||
},
|
||||
isLoading: _ioniaViewModel.createUserState is IoniaCreateStateLoading,
|
||||
isLoading: _authViewModel.createUserState is IoniaCreateStateLoading,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
|
@ -103,9 +104,9 @@ class IoniaLoginPage extends BasePage {
|
|||
});
|
||||
}
|
||||
|
||||
void _onLoginSuccessful(BuildContext context, IoniaViewModel ioniaViewModel) => Navigator.pushNamed(
|
||||
void _onLoginSuccessful(BuildContext context, IoniaAuthViewModel authViewModel) => Navigator.pushNamed(
|
||||
context,
|
||||
Routes.ioniaVerifyIoniaOtpPage,
|
||||
arguments: [ioniaViewModel.email, ioniaViewModel],
|
||||
arguments: [authViewModel.email, authViewModel],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,33 +4,34 @@ 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/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/typography.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_auth_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class IoniaVerifyIoniaOtp extends BasePage {
|
||||
|
||||
IoniaVerifyIoniaOtp(this._ioniaViewModel, this._email)
|
||||
IoniaVerifyIoniaOtp(this._authViewModel, this._email)
|
||||
: _codeController = TextEditingController(),
|
||||
_codeFocus = FocusNode() {
|
||||
_codeController.addListener(() {
|
||||
final otp = _codeController.text;
|
||||
_ioniaViewModel.otp = otp;
|
||||
_authViewModel.otp = otp;
|
||||
if (otp.length > 3) {
|
||||
_ioniaViewModel.otpState = IoniaOtpSendEnabled();
|
||||
_authViewModel.otpState = IoniaOtpSendEnabled();
|
||||
} else {
|
||||
_ioniaViewModel.otpState = IoniaOtpSendDisabled();
|
||||
_authViewModel.otpState = IoniaOtpSendDisabled();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final IoniaViewModel _ioniaViewModel;
|
||||
final IoniaAuthViewModel _authViewModel;
|
||||
|
||||
final String _email;
|
||||
|
||||
|
@ -49,7 +50,7 @@ class IoniaVerifyIoniaOtp extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
reaction((_) => _ioniaViewModel.otpState, (IoniaOtpState state) {
|
||||
reaction((_) => _authViewModel.otpState, (IoniaOtpState state) {
|
||||
if (state is IoniaOtpFailure) {
|
||||
_onOtpFailure(context, state.error);
|
||||
}
|
||||
|
@ -57,57 +58,74 @@ class IoniaVerifyIoniaOtp extends BasePage {
|
|||
_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,
|
||||
return KeyboardActions(
|
||||
config: KeyboardActionsConfig(
|
||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||
keyboardBarColor: Theme.of(context).accentTextTheme.body2.backgroundColor,
|
||||
nextFocus: false,
|
||||
actions: [
|
||||
KeyboardActionsItem(
|
||||
focusNode: _codeFocus,
|
||||
toolbarButtons: [(_) => KeyboardDoneButton()],
|
||||
),
|
||||
]),
|
||||
child: Container(
|
||||
height: 0,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.all(24),
|
||||
content: Column(
|
||||
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),
|
||||
),
|
||||
BaseTextFormField(
|
||||
hintText: S.of(context).enter_code,
|
||||
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
|
||||
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: () => _authViewModel.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,
|
||||
),
|
||||
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 _authViewModel.verifyEmail(_codeController.text),
|
||||
isDisabled: _authViewModel.otpState is IoniaOtpSendDisabled,
|
||||
isLoading: _authViewModel.otpState is IoniaOtpValidating,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@ 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/typography.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_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);
|
||||
IoniaWelcomePage(this._cardsListViewModel);
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) {
|
||||
|
@ -22,11 +22,11 @@ class IoniaWelcomePage extends BasePage {
|
|||
);
|
||||
}
|
||||
|
||||
final IoniaViewModel _ioniaViewModel;
|
||||
final IoniaGiftCardsListViewModel _cardsListViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
reaction((_) => _ioniaViewModel.isLoggedIn, (bool state) {
|
||||
reaction((_) => _cardsListViewModel.isLoggedIn, (bool state) {
|
||||
if (state) {
|
||||
Navigator.pushReplacementNamed(context, Routes.ioniaManageCardsPage);
|
||||
}
|
||||
|
|
118
lib/src/screens/ionia/cards/ionia_account_cards_page.dart
Normal file
|
@ -0,0 +1,118 @@
|
|||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/typography.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class IoniaAccountCardsPage extends BasePage {
|
||||
IoniaAccountCardsPage(this.ioniaAccountViewModel);
|
||||
|
||||
final IoniaAccountViewModel ioniaAccountViewModel;
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) {
|
||||
return Text(
|
||||
S.of(context).cards,
|
||||
style: textLargeSemiBold(
|
||||
color: Theme.of(context).accentTextTheme.display4.backgroundColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return _IoniaCardTabs();
|
||||
}
|
||||
}
|
||||
|
||||
class _IoniaCardTabs extends StatefulWidget {
|
||||
@override
|
||||
_IoniaCardTabsState createState() => _IoniaCardTabsState();
|
||||
}
|
||||
|
||||
class _IoniaCardTabsState extends State<_IoniaCardTabs> with SingleTickerProviderStateMixin {
|
||||
TabController _tabController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_tabController = TabController(length: 2, vsync: this);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_tabController.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 45,
|
||||
width: 230,
|
||||
padding: EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).accentTextTheme.display4.backgroundColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(
|
||||
25.0,
|
||||
),
|
||||
),
|
||||
child: Theme(
|
||||
data: ThemeData(
|
||||
primaryTextTheme: TextTheme(
|
||||
body2: TextStyle(backgroundColor: Colors.transparent)
|
||||
)
|
||||
),
|
||||
child: TabBar(
|
||||
controller: _tabController,
|
||||
indicator: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(
|
||||
25.0,
|
||||
),
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
),
|
||||
labelColor: Theme.of(context).primaryTextTheme.display4.backgroundColor,
|
||||
unselectedLabelColor: Theme.of(context).primaryTextTheme.title.color,
|
||||
tabs: [
|
||||
Tab(
|
||||
text: S.of(context).active,
|
||||
),
|
||||
Tab(
|
||||
text: S.of(context).redeemed,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
S.of(context).gift_card_balance_note,
|
||||
textAlign: TextAlign.center,
|
||||
style: textSmall(color: Theme.of(context).primaryTextTheme.overline.color,),
|
||||
),
|
||||
),
|
||||
|
||||
Center(
|
||||
child: Text(
|
||||
S.of(context).gift_card_redeemed_note,
|
||||
textAlign: TextAlign.center,
|
||||
style: textSmall(color: Theme.of(context).primaryTextTheme.overline.color,),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
180
lib/src/screens/ionia/cards/ionia_account_page.dart
Normal file
|
@ -0,0 +1,180 @@
|
|||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/widgets/ionia_tile.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/view_model/ionia/ionia_account_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class IoniaAccountPage extends BasePage {
|
||||
IoniaAccountPage(this.ioniaAccountViewModel);
|
||||
|
||||
final IoniaAccountViewModel ioniaAccountViewModel;
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) {
|
||||
return Text(
|
||||
S.current.account,
|
||||
style: textLargeSemiBold(
|
||||
color: Theme.of(context).accentTextTheme.display4.backgroundColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final deviceWidth = MediaQuery.of(context).size.width;
|
||||
return ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.all(24),
|
||||
content: Column(
|
||||
children: [
|
||||
_GradiantContainer(
|
||||
content: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Observer(builder: (_) =>
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
text: '${ioniaAccountViewModel.countOfMerch}',
|
||||
style: textLargeSemiBold(),
|
||||
children: [
|
||||
TextSpan(
|
||||
text: ' ${S.of(context).active_cards}',
|
||||
style: textSmall(color: Colors.white.withOpacity(0.7))),
|
||||
],
|
||||
),
|
||||
)),
|
||||
InkWell(
|
||||
onTap: () => Navigator.pushNamed(context, Routes.ioniaAccountCardsPage),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
S.of(context).view_all,
|
||||
style: textSmallSemiBold(),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
//Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// _GradiantContainer(
|
||||
// padding: EdgeInsets.all(16),
|
||||
// width: deviceWidth * 0.28,
|
||||
// content: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// S.of(context).total_saving,
|
||||
// style: textSmall(),
|
||||
// ),
|
||||
// SizedBox(height: 8),
|
||||
// Text(
|
||||
// '\$100',
|
||||
// style: textMediumSemiBold(),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// _GradiantContainer(
|
||||
// padding: EdgeInsets.all(16),
|
||||
// width: deviceWidth * 0.28,
|
||||
// content: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// S.of(context).last_30_days,
|
||||
// style: textSmall(),
|
||||
// ),
|
||||
// SizedBox(height: 8),
|
||||
// Text(
|
||||
// '\$100',
|
||||
// style: textMediumSemiBold(),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// _GradiantContainer(
|
||||
// padding: EdgeInsets.all(16),
|
||||
// width: deviceWidth * 0.28,
|
||||
// content: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// S.of(context).avg_savings,
|
||||
// style: textSmall(),
|
||||
// ),
|
||||
// SizedBox(height: 8),
|
||||
// Text(
|
||||
// '10%',
|
||||
// style: textMediumSemiBold(),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
//),
|
||||
SizedBox(height: 40),
|
||||
Observer(builder: (_) =>
|
||||
IoniaTile(
|
||||
title: S.of(context).email_address,
|
||||
subTitle: ioniaAccountViewModel.email)),
|
||||
Divider()
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.all(30),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
PrimaryButton(
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
text: S.of(context).logout,
|
||||
onPressed: () {
|
||||
ioniaAccountViewModel.logout();
|
||||
Navigator.pushNamedAndRemoveUntil(context, Routes.dashboard, (route) => false);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GradiantContainer extends StatelessWidget {
|
||||
const _GradiantContainer({
|
||||
Key key,
|
||||
@required this.content,
|
||||
this.padding,
|
||||
this.width,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget content;
|
||||
final EdgeInsets padding;
|
||||
final double width;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
child: content,
|
||||
width: width,
|
||||
padding: padding ?? EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
Theme.of(context).accentColor,
|
||||
],
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -7,16 +7,16 @@ 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:cake_wallet/view_model/ionia/ionia_gift_cards_list_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);
|
||||
IoniaActivateDebitCardPage(this._cardsListViewModel);
|
||||
|
||||
final IoniaViewModel _ioniaViewModel;
|
||||
final IoniaGiftCardsListViewModel _cardsListViewModel;
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) {
|
||||
|
@ -30,7 +30,7 @@ class IoniaActivateDebitCardPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
reaction((_) => _ioniaViewModel.createCardState, (IoniaCreateCardState state) {
|
||||
reaction((_) => _cardsListViewModel.createCardState, (IoniaCreateCardState state) {
|
||||
if (state is IoniaCreateCardFailure) {
|
||||
_onCreateCardFailure(context, state.error);
|
||||
}
|
||||
|
@ -72,9 +72,9 @@ class IoniaActivateDebitCardPage extends BasePage {
|
|||
),
|
||||
bottomSection: LoadingPrimaryButton(
|
||||
onPressed: () {
|
||||
_ioniaViewModel.createCard();
|
||||
_cardsListViewModel.createCard();
|
||||
},
|
||||
isLoading: _ioniaViewModel.createCardState is IoniaCreateCardLoading,
|
||||
isLoading: _cardsListViewModel.createCardState is IoniaCreateCardLoading,
|
||||
text: S.of(context).agree_and_continue,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
|
|
|
@ -1,24 +1,40 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_tip.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_background.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.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/src/widgets/standart_list_row.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:cake_wallet/view_model/ionia/ionia_purchase_merch_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 IoniaBuyGiftCardDetailPage extends StatelessWidget {
|
||||
IoniaBuyGiftCardDetailPage(this.amount, this.ioniaPurchaseViewModel, this.merchant) {
|
||||
ioniaPurchaseViewModel.setSelectedMerchant(merchant);
|
||||
}
|
||||
|
||||
const IoniaBuyGiftCardDetailPage(this.merchant);
|
||||
final IoniaMerchPurchaseViewModel ioniaPurchaseViewModel;
|
||||
|
||||
final IoniaMerchant merchant;
|
||||
|
||||
final String amount;
|
||||
|
||||
ThemeBase get currentTheme => getIt.get<SettingsStore>().currentTheme;
|
||||
|
||||
Color get backgroundLightColor => Colors.white;
|
||||
|
@ -57,148 +73,211 @@ class IoniaBuyGiftCardDetailPage extends StatelessWidget {
|
|||
|
||||
Widget middle(BuildContext context) {
|
||||
return Text(
|
||||
merchant.legalName,
|
||||
ioniaPurchaseViewModel.ioniaMerchant.legalName,
|
||||
style: textLargeSemiBold(color: Theme.of(context).accentTextTheme.display4.backgroundColor),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final merchant = ioniaPurchaseViewModel.ioniaMerchant;
|
||||
final _backgroundColor = currentTheme.type == ThemeType.dark ? backgroundDarkColor : backgroundLightColor;
|
||||
|
||||
reaction((_) => ioniaPurchaseViewModel.invoiceCreationState, (ExecutionState state) {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => ioniaPurchaseViewModel.invoiceCommittingState, (ExecutionState state) {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (state is ExecutedSuccessfullyState) {
|
||||
final transactionInfo = state.payload as AnyPayPaymentCommittedInfo;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
barrierColor: PaletteDark.darkNightBlue.withOpacity(0.75),
|
||||
builder: (BuildContext context) {
|
||||
return Center(
|
||||
child: _IoniaTransactionCommitedAlert(transactionInfo: transactionInfo),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
ioniaPurchaseViewModel.onAmountChanged(amount);
|
||||
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(percentage: merchant.minimumDiscount,)],
|
||||
),
|
||||
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,
|
||||
content: Observer(builder: (_) {
|
||||
final tipAmount = ioniaPurchaseViewModel.tipAmount;
|
||||
return Column(
|
||||
children: [
|
||||
SizedBox(height: 60),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
leading(context),
|
||||
middle(context),
|
||||
DiscountBadge(
|
||||
percentage: merchant.minimumDiscount,
|
||||
)
|
||||
],
|
||||
),
|
||||
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(
|
||||
'\$${ioniaPurchaseViewModel.giftCardAmount}',
|
||||
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(
|
||||
'\$$amount',
|
||||
style: textLargeSemiBold(),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).tip,
|
||||
style: textSmall(),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
'\$$tipAmount',
|
||||
style: textLargeSemiBold(),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
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(),
|
||||
),
|
||||
],
|
||||
),
|
||||
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(
|
||||
selectedTip: tipAmount,
|
||||
tipsList: [
|
||||
IoniaTip(percentage: 0, originalAmount: double.parse(amount)),
|
||||
IoniaTip(percentage: 10, originalAmount: double.parse(amount)),
|
||||
IoniaTip(percentage: 20, originalAmount: double.parse(amount)),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Divider(),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
S.of(context).you_pay,
|
||||
style: textSmall(),
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
'22.3435345000 XMR',
|
||||
style: textLargeSemiBold(),
|
||||
),
|
||||
],
|
||||
onSelect: (value) => ioniaPurchaseViewModel.addTip(value),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
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: () => _showHowToUseCard(context, merchant),
|
||||
),
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
child: Observer(builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
isLoading: ioniaPurchaseViewModel.invoiceCreationState is IsExecutingState ||
|
||||
ioniaPurchaseViewModel.invoiceCommittingState is IsExecutingState,
|
||||
isDisabled: !ioniaPurchaseViewModel.enableCardPurchase,
|
||||
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)),
|
||||
InkWell(
|
||||
onTap: () => _showTermsAndCondition(context),
|
||||
child: Text(S.of(context).settings_terms_and_conditions,
|
||||
style: textMediumSemiBold(
|
||||
color: Theme.of(context).primaryTextTheme.body1.color,
|
||||
).copyWith(fontSize: 12)),
|
||||
),
|
||||
SizedBox(height: 16)
|
||||
],
|
||||
),
|
||||
|
@ -206,117 +285,351 @@ class IoniaBuyGiftCardDetailPage extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
void purchaseCard(BuildContext context) {
|
||||
void _showTermsAndCondition(BuildContext context) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: '',
|
||||
alertContent: merchant.termsAndConditions,
|
||||
buttonText: S.of(context).agree,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> purchaseCard(BuildContext context) async {
|
||||
await ioniaPurchaseViewModel.createInvoice();
|
||||
|
||||
if (ioniaPurchaseViewModel.invoiceCreationState is ExecutedSuccessfullyState) {
|
||||
await _presentSuccessfulInvoiceCreationPopup(context);
|
||||
}
|
||||
}
|
||||
|
||||
void _showHowToUseCard(
|
||||
BuildContext context,
|
||||
IoniaMerchant merchant,
|
||||
) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return IoniaConfirmModal(
|
||||
alertTitle: S.of(context).confirm_sending,
|
||||
alertContent: SizedBox(
|
||||
//Todo:: substitute this widget with modal content
|
||||
height: 200,
|
||||
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(
|
||||
merchant.usageInstructionsBak,
|
||||
style: textMedium(
|
||||
color: Theme.of(context).textTheme.display2.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 35),
|
||||
PrimaryButton(
|
||||
onPressed: () => 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,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
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());
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _presentSuccessfulInvoiceCreationPopup(BuildContext context) async {
|
||||
final amount = ioniaPurchaseViewModel.invoice.totalAmount;
|
||||
final addresses = ioniaPurchaseViewModel.invoice.outAddresses;
|
||||
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return IoniaConfirmModal(
|
||||
alertTitle: S.of(context).confirm_sending,
|
||||
alertContent: Container(
|
||||
height: 200,
|
||||
padding: EdgeInsets.all(15),
|
||||
child: Column(children: [
|
||||
Row(children: [
|
||||
Text(S.of(context).payment_id,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none)),
|
||||
Text(ioniaPurchaseViewModel.invoice.paymentId,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none))
|
||||
], mainAxisAlignment: MainAxisAlignment.spaceBetween),
|
||||
SizedBox(height: 10),
|
||||
Row(children: [
|
||||
Text(S.of(context).amount,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none)),
|
||||
Text('$amount ${ioniaPurchaseViewModel.invoice.chain}',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none))
|
||||
], mainAxisAlignment: MainAxisAlignment.spaceBetween),
|
||||
SizedBox(height: 25),
|
||||
Row(children: [
|
||||
Text(S.of(context).recipient_address,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none))
|
||||
], mainAxisAlignment: MainAxisAlignment.center),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemBuilder: (_, int index) {
|
||||
return Text(addresses[index],
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: PaletteDark.pigeonBlue,
|
||||
decoration: TextDecoration.none));
|
||||
},
|
||||
itemCount: addresses.length,
|
||||
physics: NeverScrollableScrollPhysics()))
|
||||
])),
|
||||
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();
|
||||
await ioniaPurchaseViewModel.commitPaymentInvoice();
|
||||
},
|
||||
actionLeftButton: () => Navigator.of(context).pop());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TipButtonGroup extends StatefulWidget {
|
||||
const TipButtonGroup({
|
||||
class _IoniaTransactionCommitedAlert extends StatelessWidget {
|
||||
const _IoniaTransactionCommitedAlert({
|
||||
Key key,
|
||||
@required this.transactionInfo,
|
||||
}) : super(key: key);
|
||||
|
||||
final AnyPayPaymentCommittedInfo transactionInfo;
|
||||
|
||||
@override
|
||||
_TipButtonGroupState createState() => _TipButtonGroupState();
|
||||
Widget build(BuildContext context) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
child: Container(
|
||||
width: 327,
|
||||
height: 340,
|
||||
color: Theme.of(context).accentTextTheme.title.decorationColor,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(40, 20, 40, 0),
|
||||
child: Text(
|
||||
S.of(context).awaiting_payment_confirmation,
|
||||
textAlign: TextAlign.center,
|
||||
style: textMediumSemiBold(
|
||||
color: Theme.of(context).accentTextTheme.display4.backgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 16, bottom: 8),
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).transaction_sent,
|
||||
style: textMedium(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
).copyWith(fontWeight: FontWeight.w500),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
S.of(context).transaction_sent_notice,
|
||||
style: textMedium(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
).copyWith(fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 16, bottom: 8),
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
StandartListRow(
|
||||
title: '${S.current.transaction_details_transaction_id}:',
|
||||
value: transactionInfo.chain,
|
||||
),
|
||||
StandartListRow(
|
||||
title: '${S.current.view_in_block_explorer}:',
|
||||
value: '${S.current.view_transaction_on} XMRChain.net'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TipButtonGroupState extends State<TipButtonGroup> {
|
||||
String selectedTip;
|
||||
class TipButtonGroup extends StatelessWidget {
|
||||
const TipButtonGroup({
|
||||
Key key,
|
||||
@required this.selectedTip,
|
||||
@required this.onSelect,
|
||||
@required this.tipsList,
|
||||
}) : super(key: key);
|
||||
|
||||
final Function(IoniaTip) onSelect;
|
||||
final double selectedTip;
|
||||
final List<IoniaTip> tipsList;
|
||||
|
||||
bool _isSelected(String value) {
|
||||
return selectedTip == value;
|
||||
final tip = selectedTip.round().toString();
|
||||
return tip == 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,
|
||||
),
|
||||
...[
|
||||
for (var i = 0; i < tipsList.length; i++) ...[
|
||||
TipButton(
|
||||
isSelected: _isSelected(tipsList[i].originalAmount.toString()),
|
||||
onTap: () => onSelect(tipsList[i]),
|
||||
caption: '${tipsList[i].percentage}%',
|
||||
subTitle: '\$${tipsList[i].additionalAmount}',
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
]
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@required this.onTap,
|
||||
this.isSelected = false,
|
||||
});
|
||||
|
||||
final String caption;
|
||||
final String subTitle;
|
||||
final bool isSelected;
|
||||
final void Function() onTap;
|
||||
|
||||
@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,
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 49,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(caption,
|
||||
style: textSmallSemiBold(
|
||||
color: isSelected
|
||||
? Theme.of(context).accentTextTheme.title.color
|
||||
: Theme.of(context).primaryTextTheme.title.color)),
|
||||
if (subTitle != null) ...[
|
||||
SizedBox(height: 4),
|
||||
Text(
|
||||
subTitle,
|
||||
style: textXxSmallSemiBold(
|
||||
color: isSelected
|
||||
? Theme.of(context).accentTextTheme.title.color
|
||||
: Theme.of(context).primaryTextTheme.overline.color,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,18 +7,28 @@ 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:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class IoniaBuyGiftCardPage extends BasePage {
|
||||
IoniaBuyGiftCardPage(this.merchant)
|
||||
: _amountFieldFocus = FocusNode(),
|
||||
_amountController = TextEditingController();
|
||||
IoniaBuyGiftCardPage(
|
||||
this.ioniaPurchaseViewModel, this.merchant,
|
||||
) : _amountFieldFocus = FocusNode(),
|
||||
_amountController = TextEditingController() {
|
||||
ioniaPurchaseViewModel.setSelectedMerchant(merchant);
|
||||
_amountController.addListener(() {
|
||||
ioniaPurchaseViewModel.onAmountChanged(_amountController.text);
|
||||
});
|
||||
}
|
||||
|
||||
final IoniaMerchPurchaseViewModel ioniaPurchaseViewModel;
|
||||
final IoniaMerchant merchant;
|
||||
|
||||
|
||||
@override
|
||||
String get title => S.current.enter_amount;
|
||||
|
||||
|
@ -39,6 +49,7 @@ class IoniaBuyGiftCardPage extends BasePage {
|
|||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final _width = MediaQuery.of(context).size.width;
|
||||
final merchant = ioniaPurchaseViewModel.ioniaMerchant;
|
||||
return KeyboardActions(
|
||||
disableScroll: true,
|
||||
config: KeyboardActionsConfig(
|
||||
|
@ -98,7 +109,7 @@ class IoniaBuyGiftCardPage extends BasePage {
|
|||
left: _width / 4,
|
||||
),
|
||||
child: Text(
|
||||
'${merchant.acceptedCurrency}: ',
|
||||
'USD: ',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
@ -146,20 +157,27 @@ class IoniaBuyGiftCardPage extends BasePage {
|
|||
),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(Routes.ioniaBuyGiftCardDetailPage, arguments: [merchant] );
|
||||
},
|
||||
text: S.of(context).continue_text,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Theme.of(context)
|
||||
Observer(builder: (_) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => Navigator.of(context).pushNamed(
|
||||
Routes.ioniaBuyGiftCardDetailPage,
|
||||
arguments: [
|
||||
ioniaPurchaseViewModel.amount,
|
||||
ioniaPurchaseViewModel.ioniaMerchant,
|
||||
],
|
||||
),
|
||||
text: S.of(context).continue_text,
|
||||
isDisabled: !ioniaPurchaseViewModel.enableCardPurchase,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.headline
|
||||
.decorationColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
|
|
181
lib/src/screens/ionia/cards/ionia_custom_tip_page.dart
Normal file
|
@ -0,0 +1,181 @@
|
|||
import 'package:cake_wallet/ionia/ionia_merchant.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:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class IoniaCustomTipPage extends BasePage {
|
||||
IoniaCustomTipPage(
|
||||
this.ioniaPurchaseViewModel,
|
||||
this.billAmount,
|
||||
this.merchant,
|
||||
) : _amountFieldFocus = FocusNode(),
|
||||
_amountController = TextEditingController() {
|
||||
ioniaPurchaseViewModel.setSelectedMerchant(merchant);
|
||||
ioniaPurchaseViewModel.onAmountChanged(billAmount);
|
||||
_amountController.addListener(() {
|
||||
// ioniaPurchaseViewModel.onTipChanged(_amountController.text);
|
||||
});
|
||||
}
|
||||
|
||||
final IoniaMerchPurchaseViewModel ioniaPurchaseViewModel;
|
||||
final String billAmount;
|
||||
final IoniaMerchant merchant;
|
||||
|
||||
@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;
|
||||
final merchant = ioniaPurchaseViewModel.ioniaMerchant;
|
||||
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),
|
||||
Observer(builder: (_) {
|
||||
if (ioniaPurchaseViewModel.percentage == 0.0) {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
|
||||
return RichText(
|
||||
textAlign: TextAlign.center,
|
||||
text: TextSpan(
|
||||
text: '\$${_amountController.text}',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.headline.color,
|
||||
),
|
||||
children: [
|
||||
TextSpan(text: ' ${S.of(context).is_percentage} '),
|
||||
TextSpan(text: '${ioniaPurchaseViewModel.percentage}%'),
|
||||
TextSpan(text: ' ${S.of(context).percentageOf(billAmount)} '),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: CardItem(
|
||||
title: merchant.legalName,
|
||||
backgroundColor: Theme.of(context).accentTextTheme.display4.backgroundColor.withOpacity(0.1),
|
||||
discount: 0.0,
|
||||
titleColor: Theme.of(context).accentTextTheme.display4.backgroundColor,
|
||||
subtitleColor: Theme.of(context).hintColor,
|
||||
subTitle: merchant.isOnline ? S.of(context).online : S.of(context).offline,
|
||||
logoUrl: merchant.logoUrl,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 12),
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(_amountController.text);
|
||||
},
|
||||
text: S.of(context).add_tip,
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 30),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -8,15 +8,15 @@ 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:cake_wallet/view_model/ionia/ionia_gift_cards_list_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;
|
||||
final IoniaGiftCardsListViewModel _cardsListViewModel;
|
||||
|
||||
IoniaDebitCardPage(this._ioniaViewModel);
|
||||
IoniaDebitCardPage(this._cardsListViewModel);
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) {
|
||||
|
@ -32,7 +32,7 @@ class IoniaDebitCardPage extends BasePage {
|
|||
Widget body(BuildContext context) {
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
final cardState = _ioniaViewModel.cardState;
|
||||
final cardState = _cardsListViewModel.cardState;
|
||||
if (cardState is IoniaFetchingCard) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
|
|
@ -1,20 +1,37 @@
|
|||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_category.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
||||
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/screens/ionia/widgets/ionia_filter_modal.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_scrollbar.dart';
|
||||
import 'package:cake_wallet/themes/theme_base.dart';
|
||||
import 'package:cake_wallet/utils/debounce.dart';
|
||||
import 'package:cake_wallet/typography.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_view_model.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_filter_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class IoniaManageCardsPage extends BasePage {
|
||||
IoniaManageCardsPage(this._ioniaViewModel);
|
||||
final IoniaViewModel _ioniaViewModel;
|
||||
IoniaManageCardsPage(this._cardsListViewModel) {
|
||||
_searchController.addListener(() {
|
||||
if (_searchController.text != _cardsListViewModel.searchString) {
|
||||
_searchDebounce.run(() {
|
||||
_cardsListViewModel.searchMerchant(_searchController.text);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
final IoniaGiftCardsListViewModel _cardsListViewModel;
|
||||
|
||||
final _searchDebounce = Debounce(Duration(milliseconds: 500));
|
||||
final _searchController = TextEditingController();
|
||||
|
||||
@override
|
||||
Color get backgroundLightColor => currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
|
||||
|
@ -80,18 +97,25 @@ class IoniaManageCardsPage extends BasePage {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
return
|
||||
_TrailingIcon(
|
||||
asset: 'assets/images/profile.png',
|
||||
onPressed: () {},
|
||||
return _TrailingIcon(
|
||||
asset: 'assets/images/profile.png',
|
||||
onPressed: () => Navigator.pushNamed(context, Routes.ioniaAccountPage),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final filterIcon = InkWell(
|
||||
onTap: () async {
|
||||
final selectedFilters = await showCategoryFilter(context, _cardsListViewModel);
|
||||
_cardsListViewModel.setSelectedFilter(selectedFilters);
|
||||
},
|
||||
child: Image.asset(
|
||||
'assets/images/filter.png',
|
||||
color: Theme.of(context).textTheme.caption.decorationColor,
|
||||
));
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(14.0),
|
||||
|
@ -100,103 +124,134 @@ class IoniaManageCardsPage extends BasePage {
|
|||
Container(
|
||||
padding: EdgeInsets.only(left: 2, right: 22),
|
||||
height: 32,
|
||||
child: _SearchWidget()
|
||||
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _SearchWidget(
|
||||
controller: _searchController,
|
||||
)),
|
||||
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: Observer(builder: (_) {
|
||||
return IoniaManageCardsPageBody(scrollOffsetFromTop: _ioniaViewModel.scrollOffsetFromTop,
|
||||
ioniaMerchants: _ioniaViewModel.ioniaMerchants,
|
||||
onSetScrollOffset: (offset) => _ioniaViewModel.setScrollOffsetFromTop(offset),
|
||||
);
|
||||
}),
|
||||
child: IoniaManageCardsPageBody(
|
||||
cardsListViewModel: _cardsListViewModel,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<IoniaCategory>> showCategoryFilter(
|
||||
BuildContext context,
|
||||
IoniaGiftCardsListViewModel viewModel,
|
||||
) async {
|
||||
return await showPopUp<List<IoniaCategory>>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return IoniaFilterModal(
|
||||
filterViewModel: getIt.get<IoniaFilterViewModel>(),
|
||||
selectedCategories: viewModel.selectedFilters,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class IoniaManageCardsPageBody extends StatefulWidget {
|
||||
const IoniaManageCardsPageBody({
|
||||
Key key,
|
||||
@required this.scrollOffsetFromTop,
|
||||
@required this.ioniaMerchants,
|
||||
@required this.onSetScrollOffset,
|
||||
@required this.cardsListViewModel,
|
||||
}) : super(key: key);
|
||||
|
||||
|
||||
final List<IoniaMerchant> ioniaMerchants;
|
||||
final double scrollOffsetFromTop;
|
||||
final Function(double) onSetScrollOffset;
|
||||
final IoniaGiftCardsListViewModel cardsListViewModel;
|
||||
|
||||
@override
|
||||
_IoniaManageCardsPageBodyState createState() => _IoniaManageCardsPageBodyState();
|
||||
}
|
||||
|
||||
class _IoniaManageCardsPageBodyState extends State<IoniaManageCardsPageBody> {
|
||||
double get backgroundHeight => MediaQuery.of(context).size.height * 0.75;
|
||||
double thumbHeight = 72;
|
||||
bool get isAlwaysShowScrollThumb => merchantsList == null ? false : merchantsList.length > 3;
|
||||
|
||||
double get backgroundHeight => MediaQuery.of(context).size.height * 0.75;
|
||||
double thumbHeight = 72;
|
||||
bool get isAlwaysShowScrollThumb => merchantsList == null ? false : merchantsList.length > 3;
|
||||
|
||||
|
||||
List<IoniaMerchant> get merchantsList => widget.ioniaMerchants;
|
||||
List<IoniaMerchant> get merchantsList => widget.cardsListViewModel.ioniaMerchants;
|
||||
|
||||
final _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
@override
|
||||
void initState() {
|
||||
_scrollController.addListener(() {
|
||||
final scrollOffsetFromTop = _scrollController.hasClients
|
||||
? (_scrollController.offset / _scrollController.position.maxScrollExtent * (backgroundHeight - thumbHeight))
|
||||
: 0.0;
|
||||
widget.onSetScrollOffset(scrollOffsetFromTop);
|
||||
widget.cardsListViewModel.setScrollOffsetFromTop(scrollOffsetFromTop);
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(children: [
|
||||
ListView.separated(
|
||||
padding: EdgeInsets.only(left: 2, right: 22),
|
||||
controller: _scrollController,
|
||||
itemCount: merchantsList.length,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 4),
|
||||
itemBuilder: (_, index) {
|
||||
final merchant = merchantsList[index];
|
||||
return CardItem(
|
||||
logoUrl: merchant.logoUrl,
|
||||
onTap: () => Navigator.of(context).pushNamed(Routes.ioniaBuyGiftCardPage, arguments: [merchant]),
|
||||
title: merchant.legalName,
|
||||
subTitle: merchant.isOnline ? S.of(context).online : S.of(context).offline,
|
||||
backgroundColor: Theme.of(context).textTheme.title.backgroundColor,
|
||||
titleColor: Theme.of(context).accentTextTheme.display3.backgroundColor,
|
||||
subtitleColor: Theme.of(context).accentTextTheme.display2.backgroundColor,
|
||||
discount: merchant.minimumDiscount,
|
||||
);
|
||||
},
|
||||
),
|
||||
isAlwaysShowScrollThumb
|
||||
? CakeScrollbar(
|
||||
backgroundHeight: backgroundHeight,
|
||||
thumbHeight: thumbHeight,
|
||||
rightOffset: 1,
|
||||
width: 3,
|
||||
backgroundColor: Theme.of(context).textTheme.caption.decorationColor.withOpacity(0.05),
|
||||
thumbColor: Theme.of(context).textTheme.caption.decorationColor.withOpacity(0.5),
|
||||
fromTop: widget.scrollOffsetFromTop,
|
||||
)
|
||||
: Offstage()
|
||||
]);
|
||||
return Observer(
|
||||
builder: (_) => Stack(children: [
|
||||
ListView.separated(
|
||||
padding: EdgeInsets.only(left: 2, right: 22),
|
||||
controller: _scrollController,
|
||||
itemCount: merchantsList.length,
|
||||
separatorBuilder: (_, __) => SizedBox(height: 4),
|
||||
itemBuilder: (_, index) {
|
||||
final merchant = merchantsList[index];
|
||||
return CardItem(
|
||||
logoUrl: merchant.logoUrl,
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(Routes.ioniaBuyGiftCardPage, arguments: [merchant]);
|
||||
},
|
||||
title: merchant.legalName,
|
||||
subTitle: merchant.isOnline ? S.of(context).online : S.of(context).offline,
|
||||
backgroundColor: Theme.of(context).textTheme.title.backgroundColor,
|
||||
titleColor: Theme.of(context).accentTextTheme.display3.backgroundColor,
|
||||
subtitleColor: Theme.of(context).accentTextTheme.display2.backgroundColor,
|
||||
discount: merchant.minimumDiscount,
|
||||
);
|
||||
},
|
||||
),
|
||||
isAlwaysShowScrollThumb
|
||||
? CakeScrollbar(
|
||||
backgroundHeight: backgroundHeight,
|
||||
thumbHeight: thumbHeight,
|
||||
rightOffset: 1,
|
||||
width: 3,
|
||||
backgroundColor: Theme.of(context).textTheme.caption.decorationColor.withOpacity(0.05),
|
||||
thumbColor: Theme.of(context).textTheme.caption.decorationColor.withOpacity(0.5),
|
||||
fromTop: widget.cardsListViewModel.scrollOffsetFromTop,
|
||||
)
|
||||
: Offstage()
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SearchWidget extends StatelessWidget {
|
||||
const _SearchWidget({
|
||||
Key key,
|
||||
@required this.controller,
|
||||
}) : super(key: key);
|
||||
final TextEditingController controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -210,6 +265,7 @@ class _SearchWidget extends StatelessWidget {
|
|||
|
||||
return TextField(
|
||||
style: TextStyle(color: Colors.white),
|
||||
controller: controller,
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
contentPadding: EdgeInsets.only(
|
||||
|
@ -268,5 +324,3 @@ class _TrailingIcon extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ class IoniaConfirmModal extends StatelessWidget {
|
|||
@required this.actionRightButton,
|
||||
this.leftActionColor,
|
||||
this.rightActionColor,
|
||||
this.hideActions = false,
|
||||
});
|
||||
|
||||
final String alertTitle;
|
||||
|
@ -23,6 +24,7 @@ class IoniaConfirmModal extends StatelessWidget {
|
|||
final VoidCallback actionRightButton;
|
||||
final Color leftActionColor;
|
||||
final Color rightActionColor;
|
||||
final bool hideActions;
|
||||
|
||||
Widget actionButtons(BuildContext context) {
|
||||
return Row(
|
||||
|
|
134
lib/src/screens/ionia/widgets/ionia_filter_modal.dart
Normal file
|
@ -0,0 +1,134 @@
|
|||
import 'package:cake_wallet/ionia/ionia_category.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/widgets/rounded_checkbox.dart';
|
||||
import 'package:cake_wallet/view_model/ionia/ionia_filter_view_model.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_background.dart';
|
||||
import 'package:cake_wallet/typography.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class IoniaFilterModal extends StatelessWidget {
|
||||
IoniaFilterModal({
|
||||
@required this.filterViewModel,
|
||||
@required this.selectedCategories,
|
||||
}) {
|
||||
filterViewModel.setSelectedCategories(this.selectedCategories);
|
||||
}
|
||||
|
||||
final IoniaFilterViewModel filterViewModel;
|
||||
final List<IoniaCategory> selectedCategories;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final searchIcon = Padding(
|
||||
padding: EdgeInsets.all(10),
|
||||
child: Image.asset(
|
||||
'assets/images/search_icon.png',
|
||||
color: Theme.of(context).accentColor,
|
||||
),
|
||||
);
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: AlertBackground(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 24, bottom: 20),
|
||||
margin: EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 40,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24),
|
||||
child: TextField(
|
||||
onChanged: filterViewModel.onSearchFilter,
|
||||
style: textMedium(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
prefixIcon: searchIcon,
|
||||
hintText: S.of(context).search_category,
|
||||
contentPadding: EdgeInsets.only(bottom: 5),
|
||||
fillColor: Theme.of(context).textTheme.subhead.backgroundColor,
|
||||
border: OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Divider(thickness: 2),
|
||||
SizedBox(height: 24),
|
||||
Observer(builder: (_) {
|
||||
return ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
shrinkWrap: true,
|
||||
itemCount: filterViewModel.ioniaCategories.length,
|
||||
itemBuilder: (_, index) {
|
||||
final category = filterViewModel.ioniaCategories[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
child: InkWell(
|
||||
onTap: () => filterViewModel.selectFilter(category),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Image.asset(
|
||||
category.iconPath,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
Text(category.title,
|
||||
style: textSmall(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
).copyWith(fontWeight: FontWeight.w500)),
|
||||
],
|
||||
),
|
||||
Observer(builder: (_) {
|
||||
final value = filterViewModel.selectedIndices;
|
||||
return RoundedCheckbox(
|
||||
value: value.contains(category.index),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () => Navigator.pop(context, filterViewModel.selectedCategories),
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(bottom: 40),
|
||||
child: CircleAvatar(
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
color: Colors.black,
|
||||
),
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
58
lib/src/screens/ionia/widgets/ionia_tile.dart
Normal file
|
@ -0,0 +1,58 @@
|
|||
import 'package:cake_wallet/typography.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class IoniaTile extends StatelessWidget {
|
||||
const IoniaTile({
|
||||
Key key,
|
||||
@required this.title,
|
||||
@required this.subTitle,
|
||||
this.trailing,
|
||||
this.onTapTrailing,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget trailing;
|
||||
final VoidCallback onTapTrailing;
|
||||
final String title;
|
||||
final String subTitle;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: textXSmall(
|
||||
color: Theme.of(context).primaryTextTheme.overline.color,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
subTitle,
|
||||
style: textMediumBold(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing != null
|
||||
? InkWell(
|
||||
onTap: () => onTapTrailing,
|
||||
child: Center(
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).accentTextTheme.display4.backgroundColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(4)),
|
||||
child: trailing,
|
||||
),
|
||||
),
|
||||
)
|
||||
: Offstage(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
27
lib/src/screens/ionia/widgets/rounded_checkbox.dart
Normal file
|
@ -0,0 +1,27 @@
|
|||
import 'dart:ui';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RoundedCheckbox extends StatelessWidget {
|
||||
RoundedCheckbox({Key key, @required this.value}) : super(key: key);
|
||||
|
||||
final bool value;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return value
|
||||
? Container(
|
||||
height: 20.0,
|
||||
width: 20.0,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(50.0)),
|
||||
color: Theme.of(context).accentTextTheme.body2.color,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
size: 14.0,
|
||||
))
|
||||
: Offstage();
|
||||
}
|
||||
}
|
36
lib/view_model/ionia/ionia_account_view_model.dart
Normal file
|
@ -0,0 +1,36 @@
|
|||
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_service.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'ionia_account_view_model.g.dart';
|
||||
|
||||
class IoniaAccountViewModel = IoniaAccountViewModelBase with _$IoniaAccountViewModel;
|
||||
|
||||
abstract class IoniaAccountViewModelBase with Store {
|
||||
|
||||
IoniaAccountViewModelBase({this.ioniaService}) {
|
||||
email = '';
|
||||
merchs = [];
|
||||
ioniaService.getUserEmail()
|
||||
.then((email) => this.email = email);
|
||||
ioniaService.getCurrentUserGiftCardSummaries()
|
||||
.then((merchs) => this.merchs = merchs);
|
||||
}
|
||||
|
||||
final IoniaService ioniaService;
|
||||
|
||||
@observable
|
||||
String email;
|
||||
|
||||
@observable
|
||||
List<IoniaMerchant> merchs;
|
||||
|
||||
@computed
|
||||
int get countOfMerch => merchs.where((merch) => merch.isActive).length;
|
||||
|
||||
@action
|
||||
void logout(){
|
||||
ioniaService.logout();
|
||||
}
|
||||
|
||||
}
|
55
lib/view_model/ionia/ionia_auth_view_model.dart
Normal file
|
@ -0,0 +1,55 @@
|
|||
import 'package:cake_wallet/ionia/ionia_create_state.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_service.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'ionia_auth_view_model.g.dart';
|
||||
|
||||
class IoniaAuthViewModel = IoniaAuthViewModelBase with _$IoniaAuthViewModel;
|
||||
|
||||
abstract class IoniaAuthViewModelBase with Store {
|
||||
|
||||
IoniaAuthViewModelBase({this.ioniaService}):
|
||||
createUserState = IoniaCreateStateSuccess(),
|
||||
otpState = IoniaOtpSendDisabled(){
|
||||
|
||||
|
||||
}
|
||||
|
||||
final IoniaService ioniaService;
|
||||
|
||||
@observable
|
||||
IoniaCreateAccountState createUserState;
|
||||
|
||||
@observable
|
||||
IoniaOtpState otpState;
|
||||
|
||||
@observable
|
||||
String email;
|
||||
|
||||
@observable
|
||||
String otp;
|
||||
|
||||
@action
|
||||
Future<void> verifyEmail(String code) async {
|
||||
try {
|
||||
otpState = IoniaOtpValidating();
|
||||
await ioniaService.verifyEmail(code);
|
||||
otpState = IoniaOtpSuccess();
|
||||
} catch (_) {
|
||||
otpState = IoniaOtpFailure(error: 'Invalid OTP. Try again');
|
||||
}
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
58
lib/view_model/ionia/ionia_filter_view_model.dart
Normal file
|
@ -0,0 +1,58 @@
|
|||
import 'package:cake_wallet/ionia/ionia_category.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'ionia_filter_view_model.g.dart';
|
||||
|
||||
class IoniaFilterViewModel = IoniaFilterViewModelBase with _$IoniaFilterViewModel;
|
||||
|
||||
abstract class IoniaFilterViewModelBase with Store {
|
||||
IoniaFilterViewModelBase() {
|
||||
selectedIndices = ObservableList<int>();
|
||||
ioniaCategories = IoniaCategory.allCategories;
|
||||
}
|
||||
|
||||
List<IoniaCategory> get selectedCategories => ioniaCategories.where(_isSelected).toList();
|
||||
|
||||
@observable
|
||||
ObservableList<int> selectedIndices;
|
||||
|
||||
@observable
|
||||
List<IoniaCategory> ioniaCategories;
|
||||
|
||||
@action
|
||||
void selectFilter(IoniaCategory ioniaCategory) {
|
||||
if (ioniaCategory == IoniaCategory.all && !selectedIndices.contains(0)) {
|
||||
selectedIndices.clear();
|
||||
selectedIndices.add(0);
|
||||
return;
|
||||
}
|
||||
if (selectedIndices.contains(ioniaCategory.index) && ioniaCategory.index != 0) {
|
||||
selectedIndices.remove(ioniaCategory.index);
|
||||
return;
|
||||
}
|
||||
selectedIndices.add(ioniaCategory.index);
|
||||
selectedIndices.remove(0);
|
||||
}
|
||||
|
||||
@action
|
||||
void onSearchFilter(String text) {
|
||||
if (text.isEmpty) {
|
||||
ioniaCategories = IoniaCategory.allCategories;
|
||||
} else {
|
||||
ioniaCategories = IoniaCategory.allCategories
|
||||
.where(
|
||||
(e) => e.title.toLowerCase().contains(text.toLowerCase()),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void setSelectedCategories(List<IoniaCategory> selectedCategories) {
|
||||
selectedIndices = ObservableList.of(selectedCategories.map((e) => e.index));
|
||||
}
|
||||
|
||||
bool _isSelected(IoniaCategory ioniaCategory) {
|
||||
return selectedIndices.contains(ioniaCategory.index);
|
||||
}
|
||||
}
|
|
@ -1,36 +1,38 @@
|
|||
import 'package:cake_wallet/ionia/ionia_category.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_service.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_create_state.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_virtual_card.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
part 'ionia_view_model.g.dart';
|
||||
part 'ionia_gift_cards_list_view_model.g.dart';
|
||||
|
||||
class IoniaViewModel = IoniaViewModelBase with _$IoniaViewModel;
|
||||
class IoniaGiftCardsListViewModel = IoniaGiftCardsListViewModelBase with _$IoniaGiftCardsListViewModel;
|
||||
|
||||
abstract class IoniaViewModelBase with Store {
|
||||
IoniaViewModelBase({this.ioniaService})
|
||||
: createUserState = IoniaCreateStateSuccess(),
|
||||
otpState = IoniaOtpSendDisabled(),
|
||||
abstract class IoniaGiftCardsListViewModelBase with Store {
|
||||
IoniaGiftCardsListViewModelBase({
|
||||
@required this.ioniaService,
|
||||
}) :
|
||||
cardState = IoniaNoCardState(),
|
||||
ioniaMerchants = [],
|
||||
scrollOffsetFromTop = 0.0 {
|
||||
_getMerchants().then((value) {
|
||||
ioniaMerchants = value;
|
||||
});
|
||||
_getAuthStatus().then((value) => isLoggedIn = value);
|
||||
selectedFilters = [];
|
||||
_getAuthStatus().then((value) => isLoggedIn = value);
|
||||
|
||||
_getMerchants();
|
||||
}
|
||||
|
||||
final IoniaService ioniaService;
|
||||
|
||||
List<IoniaMerchant> ioniaMerchantList;
|
||||
|
||||
String searchString;
|
||||
|
||||
List<IoniaCategory> selectedFilters;
|
||||
|
||||
@observable
|
||||
double scrollOffsetFromTop;
|
||||
|
||||
@observable
|
||||
IoniaCreateAccountState createUserState;
|
||||
|
||||
@observable
|
||||
IoniaOtpState otpState;
|
||||
|
||||
@observable
|
||||
IoniaCreateCardState createCardState;
|
||||
|
||||
|
@ -40,38 +42,9 @@ abstract class IoniaViewModelBase with Store {
|
|||
@observable
|
||||
List<IoniaMerchant> ioniaMerchants;
|
||||
|
||||
@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();
|
||||
}
|
||||
|
@ -89,6 +62,18 @@ abstract class IoniaViewModelBase with Store {
|
|||
return null;
|
||||
}
|
||||
|
||||
@action
|
||||
void searchMerchant(String text) {
|
||||
if (text.isEmpty) {
|
||||
ioniaMerchants = ioniaMerchantList;
|
||||
return;
|
||||
}
|
||||
searchString = text;
|
||||
ioniaService.getMerchantsByFilter(search: searchString).then((value) {
|
||||
ioniaMerchants = value;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _getCard() async {
|
||||
cardState = IoniaFetchingCard();
|
||||
try {
|
||||
|
@ -100,8 +85,16 @@ abstract class IoniaViewModelBase with Store {
|
|||
}
|
||||
}
|
||||
|
||||
Future<List<IoniaMerchant>> _getMerchants() async {
|
||||
return await ioniaService.getMerchants();
|
||||
void _getMerchants() {
|
||||
ioniaService.getMerchantsByFilter(categories: selectedFilters).then((value) {
|
||||
ioniaMerchants = ioniaMerchantList = value;
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
void setSelectedFilter(List<IoniaCategory> filters) {
|
||||
selectedFilters = filters;
|
||||
_getMerchants();
|
||||
}
|
||||
|
||||
void setScrollOffsetFromTop(double scrollOffset) {
|
91
lib/view_model/ionia/ionia_purchase_merch_view_model.dart
Normal file
|
@ -0,0 +1,91 @@
|
|||
import 'package:cake_wallet/anypay/any_pay_payment.dart';
|
||||
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_anypay.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_merchant.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_tip.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'ionia_purchase_merch_view_model.g.dart';
|
||||
|
||||
class IoniaMerchPurchaseViewModel = IoniaMerchPurchaseViewModelBase with _$IoniaMerchPurchaseViewModel;
|
||||
|
||||
abstract class IoniaMerchPurchaseViewModelBase with Store {
|
||||
IoniaMerchPurchaseViewModelBase(this.ioniaAnyPayService) {
|
||||
tipAmount = 0.0;
|
||||
percentage = 0.0;
|
||||
amount = '';
|
||||
enableCardPurchase = false;
|
||||
}
|
||||
|
||||
IoniaMerchant ioniaMerchant;
|
||||
|
||||
IoniaAnyPay ioniaAnyPayService;
|
||||
|
||||
AnyPayPayment invoice;
|
||||
|
||||
AnyPayPaymentCommittedInfo committedInfo;
|
||||
|
||||
@observable
|
||||
ExecutionState invoiceCreationState;
|
||||
|
||||
@observable
|
||||
ExecutionState invoiceCommittingState;
|
||||
|
||||
@observable
|
||||
String amount;
|
||||
|
||||
@observable
|
||||
double percentage;
|
||||
|
||||
@computed
|
||||
double get giftCardAmount => double.parse(amount) + tipAmount;
|
||||
|
||||
@observable
|
||||
double tipAmount;
|
||||
|
||||
@observable
|
||||
bool enableCardPurchase;
|
||||
|
||||
@action
|
||||
void onAmountChanged(String input) {
|
||||
if (input.isEmpty) return;
|
||||
amount = input;
|
||||
final inputAmount = double.parse(input);
|
||||
final min = ioniaMerchant.minimumCardPurchase;
|
||||
final max = ioniaMerchant.maximumCardPurchase;
|
||||
|
||||
enableCardPurchase = inputAmount >= min && inputAmount <= max;
|
||||
}
|
||||
|
||||
void setSelectedMerchant(IoniaMerchant merchant) {
|
||||
ioniaMerchant = merchant;
|
||||
}
|
||||
|
||||
@action
|
||||
void addTip(IoniaTip tip) {
|
||||
tipAmount = tip.additionalAmount;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> createInvoice() async {
|
||||
try {
|
||||
invoiceCreationState = IsExecutingState();
|
||||
invoice = await ioniaAnyPayService.purchase(merchId: ioniaMerchant.id.toString(), amount: giftCardAmount);
|
||||
invoiceCreationState = ExecutedSuccessfullyState();
|
||||
} catch (e) {
|
||||
invoiceCreationState = FailureState(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> commitPaymentInvoice() async {
|
||||
try {
|
||||
invoiceCommittingState = IsExecutingState();
|
||||
committedInfo = await ioniaAnyPayService.commitInvoice(invoice);
|
||||
invoiceCommittingState = ExecutedSuccessfullyState(payload: committedInfo);
|
||||
} catch (e) {
|
||||
invoiceCommittingState = FailureState(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -596,5 +596,28 @@
|
|||
"mm": "MM",
|
||||
"yy": "YY",
|
||||
"online": "Online",
|
||||
"offline": "Offline"
|
||||
"offline": "Offline",
|
||||
"gift_card_number": "Gift card number",
|
||||
"pin_number": "PIN number",
|
||||
"total_saving": "Total Savings",
|
||||
"last_30_days": "Last 30 days",
|
||||
"avg_savings": "Avg. savings",
|
||||
"view_all": "View all",
|
||||
"active_cards": "Active cards",
|
||||
"delete_account": "Delete Account",
|
||||
"cards": "Cards",
|
||||
"active": "Active",
|
||||
"redeemed": "Redeemed",
|
||||
"gift_card_balance_note": "Gift cards with a balance remaining will appear here",
|
||||
"gift_card_redeemed_note": "Gift cards you’ve redeemed will appear here",
|
||||
"logout": "Logout",
|
||||
"add_tip": "Add Tip",
|
||||
"percentageOf": "of ${amount}",
|
||||
"is_percentage": "is",
|
||||
"search_category": "Search category",
|
||||
"mark_as_redeemed": "Mark As Redeemed",
|
||||
"more_options": "More Options",
|
||||
"awaiting_payment_confirmation": "Awaiting payment confirmation",
|
||||
"transaction_sent_notice": "If the screen doesn’t proceed after 1 minute, check a block explorer and your email.",
|
||||
"agree": "Agree"
|
||||
}
|
||||
|
|