mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-23 03:59:23 +00:00
CW-240 Receive fiat currency amount and receive animations (#877)
* Redesign receive amount field * Fix issues with animations * Fix issues with animations * Fix max fraction digit to 8 * add another 0 * Update amount when currency is changed --------- Co-authored-by: Justin Ehrenhofer <justin.ehrenhofer@gmail.com> Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
8ffac75e8c
commit
f2b8dd21a1
13 changed files with 504 additions and 340 deletions
|
@ -1,6 +1,7 @@
|
||||||
import 'package:cake_wallet/core/validator.dart';
|
import 'package:cake_wallet/core/validator.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cw_core/currency.dart';
|
||||||
|
|
||||||
class AmountValidator extends TextValidator {
|
class AmountValidator extends TextValidator {
|
||||||
AmountValidator({
|
AmountValidator({
|
||||||
|
@ -57,7 +58,7 @@ class SymbolsAmountValidator extends TextValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
class DecimalAmountValidator extends TextValidator {
|
class DecimalAmountValidator extends TextValidator {
|
||||||
DecimalAmountValidator({required CryptoCurrency currency, required bool isAutovalidate })
|
DecimalAmountValidator({required Currency currency, required bool isAutovalidate })
|
||||||
: super(
|
: super(
|
||||||
errorMessage: S.current.decimal_places_error,
|
errorMessage: S.current.decimal_places_error,
|
||||||
pattern: _pattern(currency),
|
pattern: _pattern(currency),
|
||||||
|
@ -65,7 +66,7 @@ class DecimalAmountValidator extends TextValidator {
|
||||||
minLength: 0,
|
minLength: 0,
|
||||||
maxLength: 0);
|
maxLength: 0);
|
||||||
|
|
||||||
static String _pattern(CryptoCurrency currency) {
|
static String _pattern(Currency currency) {
|
||||||
switch (currency) {
|
switch (currency) {
|
||||||
case CryptoCurrency.xmr:
|
case CryptoCurrency.xmr:
|
||||||
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,12})\$';
|
return '^([0-9]+([.\,][0-9]{1,12})?|[.\,][0-9]{1,12})\$';
|
||||||
|
|
|
@ -183,6 +183,7 @@ import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
||||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
|
|
||||||
final getIt = GetIt.instance;
|
final getIt = GetIt.instance;
|
||||||
|
|
||||||
|
@ -321,7 +322,9 @@ Future setup(
|
||||||
|
|
||||||
getIt.registerFactory<WalletAddressListViewModel>(() =>
|
getIt.registerFactory<WalletAddressListViewModel>(() =>
|
||||||
WalletAddressListViewModel(
|
WalletAddressListViewModel(
|
||||||
appStore: getIt.get<AppStore>(), yatStore: getIt.get<YatStore>()));
|
appStore: getIt.get<AppStore>(), yatStore: getIt.get<YatStore>(),
|
||||||
|
fiatConversionStore: getIt.get<FiatConversionStore>()
|
||||||
|
));
|
||||||
|
|
||||||
getIt.registerFactory(() => BalanceViewModel(
|
getIt.registerFactory(() => BalanceViewModel(
|
||||||
appStore: getIt.get<AppStore>(),
|
appStore: getIt.get<AppStore>(),
|
||||||
|
@ -815,8 +818,8 @@ Future setup(
|
||||||
getIt.registerFactory(() => AddressResolver(yatService: getIt.get<YatService>(),
|
getIt.registerFactory(() => AddressResolver(yatService: getIt.get<YatService>(),
|
||||||
walletType: getIt.get<AppStore>().wallet!.type));
|
walletType: getIt.get<AppStore>().wallet!.type));
|
||||||
|
|
||||||
getIt.registerFactoryParam<FullscreenQRPage, String, int?>(
|
getIt.registerFactoryParam<FullscreenQRPage, QrViewData, void>(
|
||||||
(String qrData, int? version) => FullscreenQRPage(qrData: qrData, version: version,));
|
(QrViewData viewData, _) => FullscreenQRPage(qrViewData: viewData));
|
||||||
|
|
||||||
getIt.registerFactory(() => IoniaApi());
|
getIt.registerFactory(() => IoniaApi());
|
||||||
|
|
||||||
|
|
11
lib/entities/qr_view_data.dart
Normal file
11
lib/entities/qr_view_data.dart
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class QrViewData {
|
||||||
|
final int? version;
|
||||||
|
final String? heroTag;
|
||||||
|
final String data;
|
||||||
|
|
||||||
|
QrViewData({
|
||||||
|
this.version,
|
||||||
|
this.heroTag,
|
||||||
|
required this.data,
|
||||||
|
});
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
import 'package:cake_wallet/entities/contact_record.dart';
|
||||||
import 'package:cake_wallet/buy/order.dart';
|
import 'package:cake_wallet/buy/order.dart';
|
||||||
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
|
import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
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/backup/edit_backup_password_page.dart';
|
||||||
|
@ -242,7 +243,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.receive:
|
case Routes.receive:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
fullscreenDialog: true, builder: (_) => getIt.get<ReceivePage>());
|
builder: (_) => getIt.get<ReceivePage>());
|
||||||
|
|
||||||
case Routes.addressPage:
|
case Routes.addressPage:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
@ -451,14 +452,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
param1: args));
|
param1: args));
|
||||||
|
|
||||||
case Routes.fullscreenQR:
|
case Routes.fullscreenQR:
|
||||||
final args = settings.arguments as Map<String, dynamic>;
|
|
||||||
|
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) =>
|
builder: (_) =>
|
||||||
getIt.get<FullscreenQRPage>(
|
getIt.get<FullscreenQRPage>(
|
||||||
param1: args['qrData'] as String,
|
param1: settings.arguments as QrViewData,
|
||||||
param2: args['version'] as int?,
|
|
||||||
|
|
||||||
));
|
));
|
||||||
|
|
||||||
case Routes.ioniaWelcomePage:
|
case Routes.ioniaWelcomePage:
|
||||||
|
|
|
@ -26,11 +26,23 @@ class AddressPage extends BasePage {
|
||||||
required this.addressListViewModel,
|
required this.addressListViewModel,
|
||||||
required this.dashboardViewModel,
|
required this.dashboardViewModel,
|
||||||
required this.receiveOptionViewModel,
|
required this.receiveOptionViewModel,
|
||||||
}) : _cryptoAmountFocus = FocusNode();
|
}) : _cryptoAmountFocus = FocusNode(),
|
||||||
|
_formKey = GlobalKey<FormState>(),
|
||||||
|
_amountController = TextEditingController(){
|
||||||
|
_amountController.addListener(() {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
addressListViewModel.changeAmount(
|
||||||
|
_amountController.text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final WalletAddressListViewModel addressListViewModel;
|
final WalletAddressListViewModel addressListViewModel;
|
||||||
final DashboardViewModel dashboardViewModel;
|
final DashboardViewModel dashboardViewModel;
|
||||||
final ReceiveOptionViewModel receiveOptionViewModel;
|
final ReceiveOptionViewModel receiveOptionViewModel;
|
||||||
|
final TextEditingController _amountController;
|
||||||
|
final GlobalKey<FormState> _formKey;
|
||||||
|
|
||||||
final FocusNode _cryptoAmountFocus;
|
final FocusNode _cryptoAmountFocus;
|
||||||
|
|
||||||
|
@ -69,28 +81,27 @@ class AddressPage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget? trailing(BuildContext context) {
|
Widget? trailing(BuildContext context) {
|
||||||
final shareImage = Image.asset('assets/images/share.png',
|
return Material(
|
||||||
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!);
|
color: Colors.transparent,
|
||||||
|
child: IconButton(
|
||||||
return !addressListViewModel.hasAddressList
|
padding: EdgeInsets.zero,
|
||||||
? Material(
|
constraints: BoxConstraints(),
|
||||||
color: Colors.transparent,
|
highlightColor: Colors.transparent,
|
||||||
child: IconButton(
|
splashColor: Colors.transparent,
|
||||||
padding: EdgeInsets.zero,
|
iconSize: 25,
|
||||||
constraints: BoxConstraints(),
|
onPressed: () {
|
||||||
highlightColor: Colors.transparent,
|
ShareUtil.share(
|
||||||
splashColor: Colors.transparent,
|
text: addressListViewModel.uri.toString(),
|
||||||
iconSize: 25,
|
context: context,
|
||||||
onPressed: () {
|
);
|
||||||
ShareUtil.share(
|
},
|
||||||
text: addressListViewModel.address.address,
|
icon: Icon(
|
||||||
context: context,
|
Icons.share,
|
||||||
);
|
size: 20,
|
||||||
},
|
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
|
||||||
icon: shareImage,
|
),
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -137,16 +148,18 @@ class AddressPage extends BasePage {
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
padding: EdgeInsets.fromLTRB(24, 0, 24, 32),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Observer(builder: (_) => QRWidget(
|
child: Observer(
|
||||||
addressListViewModel: addressListViewModel,
|
builder: (_) => QRWidget(
|
||||||
amountTextFieldFocusNode: _cryptoAmountFocus,
|
formKey: _formKey,
|
||||||
isAmountFieldShow: !addressListViewModel.hasAccounts,
|
addressListViewModel: addressListViewModel,
|
||||||
isLight: dashboardViewModel.settingsStore.currentTheme.type == ThemeType.light))
|
amountTextFieldFocusNode: _cryptoAmountFocus,
|
||||||
),
|
amountController: _amountController,
|
||||||
|
isLight: dashboardViewModel.settingsStore.currentTheme.type ==
|
||||||
|
ThemeType.light))),
|
||||||
Observer(builder: (_) {
|
Observer(builder: (_) {
|
||||||
return addressListViewModel.hasAddressList
|
return addressListViewModel.hasAddressList
|
||||||
? GestureDetector(
|
? GestureDetector(
|
||||||
|
|
|
@ -115,10 +115,6 @@ class ExchangePage extends BasePage {
|
||||||
WidgetsBinding.instance
|
WidgetsBinding.instance
|
||||||
.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
|
.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
|
||||||
|
|
||||||
if (exchangeViewModel.isLowFee) {
|
|
||||||
_showFeeAlert(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
return KeyboardActions(
|
return KeyboardActions(
|
||||||
disableScroll: true,
|
disableScroll: true,
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
|
@ -319,6 +315,10 @@ class ExchangePage extends BasePage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exchangeViewModel.isLowFee) {
|
||||||
|
_showFeeAlert(context);
|
||||||
|
}
|
||||||
|
|
||||||
final depositAddressController = depositKey.currentState!.addressController;
|
final depositAddressController = depositKey.currentState!.addressController;
|
||||||
final depositAmountController = depositKey.currentState!.amountController;
|
final depositAmountController = depositKey.currentState!.amountController;
|
||||||
final receiveAddressController = receiveKey.currentState!.addressController;
|
final receiveAddressController = receiveKey.currentState!.addressController;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
|
||||||
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
|
||||||
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/entities/receive_page_option.dart';
|
import 'package:cake_wallet/entities/receive_page_option.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -133,10 +134,9 @@ class AnonPayReceivePage extends BasePage {
|
||||||
await Navigator.pushNamed(
|
await Navigator.pushNamed(
|
||||||
context,
|
context,
|
||||||
Routes.fullscreenQR,
|
Routes.fullscreenQR,
|
||||||
arguments: {
|
arguments: QrViewData(data: invoiceInfo.clearnetUrl,
|
||||||
'qrData': invoiceInfo.clearnetUrl,
|
version: qr.QrVersions.auto,
|
||||||
'version': qr.QrVersions.auto,
|
)
|
||||||
},
|
|
||||||
);
|
);
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
DeviceDisplayBrightness.setBrightness(brightness);
|
DeviceDisplayBrightness.setBrightness(brightness);
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
|
||||||
class FullscreenQRPage extends BasePage {
|
class FullscreenQRPage extends BasePage {
|
||||||
FullscreenQRPage({required this.qrData, int? this.version});
|
FullscreenQRPage({required this.qrViewData});
|
||||||
|
|
||||||
final String qrData;
|
final QrViewData qrViewData;
|
||||||
final int? version;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Color get backgroundLightColor => currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
|
Color get backgroundLightColor => currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
|
||||||
|
@ -63,7 +63,7 @@ class FullscreenQRPage extends BasePage {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.05),
|
padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.05),
|
||||||
child: Hero(
|
child: Hero(
|
||||||
tag: Key(qrData),
|
tag: Key(qrViewData.heroTag ?? qrViewData.data),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1.0,
|
aspectRatio: 1.0,
|
||||||
|
@ -71,7 +71,7 @@ class FullscreenQRPage extends BasePage {
|
||||||
padding: EdgeInsets.all(10),
|
padding: EdgeInsets.all(10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(width: 3, color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!)),
|
border: Border.all(width: 3, color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!)),
|
||||||
child: QrImage(data: qrData, version: version),
|
child: QrImage(data: qrViewData.data, version: qrViewData.version),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -21,16 +21,28 @@ import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||||
|
|
||||||
class ReceivePage extends BasePage {
|
class ReceivePage extends BasePage {
|
||||||
ReceivePage({required this.addressListViewModel}) : _cryptoAmountFocus = FocusNode();
|
ReceivePage({required this.addressListViewModel})
|
||||||
|
: _cryptoAmountFocus = FocusNode(),
|
||||||
|
_amountController = TextEditingController(),
|
||||||
|
_formKey = GlobalKey<FormState>() {
|
||||||
|
_amountController.addListener(() {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
addressListViewModel.changeAmount(_amountController.text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final WalletAddressListViewModel addressListViewModel;
|
final WalletAddressListViewModel addressListViewModel;
|
||||||
|
final TextEditingController _amountController;
|
||||||
|
final GlobalKey<FormState> _formKey;
|
||||||
|
static const _heroTag = 'receive_page';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.receive;
|
String get title => S.current.receive;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Color get backgroundLightColor => currentTheme.type == ThemeType.bright
|
Color get backgroundLightColor =>
|
||||||
? Colors.transparent : Colors.white;
|
currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Color get backgroundDarkColor => Colors.transparent;
|
Color get backgroundDarkColor => Colors.transparent;
|
||||||
|
@ -68,162 +80,153 @@ class ReceivePage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget trailing(BuildContext context) {
|
Widget trailing(BuildContext context) {
|
||||||
final shareImage =
|
|
||||||
Image.asset('assets/images/share.png',
|
|
||||||
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!);
|
|
||||||
|
|
||||||
return Material(
|
return Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: Semantics(
|
child: Semantics(
|
||||||
label: 'Share',
|
label: 'Share',
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
constraints: BoxConstraints(),
|
constraints: BoxConstraints(),
|
||||||
highlightColor: Colors.transparent,
|
highlightColor: Colors.transparent,
|
||||||
splashColor: Colors.transparent,
|
splashColor: Colors.transparent,
|
||||||
iconSize: 25,
|
iconSize: 25,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ShareUtil.share(
|
ShareUtil.share(
|
||||||
text: addressListViewModel.address.address,
|
text: addressListViewModel.uri.toString(),
|
||||||
context: context,
|
context: context,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: shareImage
|
icon: Icon(
|
||||||
|
Icons.share,
|
||||||
|
size: 20,
|
||||||
|
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
return (addressListViewModel.type == WalletType.monero || addressListViewModel.type == WalletType.haven)
|
return (addressListViewModel.type == WalletType.monero ||
|
||||||
|
addressListViewModel.type == WalletType.haven)
|
||||||
? KeyboardActions(
|
? KeyboardActions(
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1!
|
keyboardBarColor: Theme.of(context).accentTextTheme!.bodyText1!.backgroundColor!,
|
||||||
.backgroundColor!,
|
nextFocus: false,
|
||||||
nextFocus: false,
|
actions: [
|
||||||
actions: [
|
KeyboardActionsItem(
|
||||||
KeyboardActionsItem(
|
focusNode: _cryptoAmountFocus,
|
||||||
focusNode: _cryptoAmountFocus,
|
toolbarButtons: [(_) => KeyboardDoneButton()],
|
||||||
toolbarButtons: [(_) => KeyboardDoneButton()],
|
)
|
||||||
)
|
]),
|
||||||
]),
|
child: SingleChildScrollView(
|
||||||
child: SingleChildScrollView(
|
child: Column(
|
||||||
child: Column(
|
children: <Widget>[
|
||||||
children: <Widget>[
|
Padding(
|
||||||
Padding(
|
padding: EdgeInsets.fromLTRB(24, 50, 24, 24),
|
||||||
padding: EdgeInsets.fromLTRB(24, 80, 24, 24),
|
child: QRWidget(
|
||||||
child: QRWidget(
|
addressListViewModel: addressListViewModel,
|
||||||
addressListViewModel: addressListViewModel,
|
formKey: _formKey,
|
||||||
isAmountFieldShow: true,
|
heroTag: _heroTag,
|
||||||
amountTextFieldFocusNode: _cryptoAmountFocus,
|
amountTextFieldFocusNode: _cryptoAmountFocus,
|
||||||
isLight: currentTheme.type == ThemeType.light),
|
amountController: _amountController,
|
||||||
|
isLight: currentTheme.type == ThemeType.light),
|
||||||
|
),
|
||||||
|
Observer(
|
||||||
|
builder: (_) => ListView.separated(
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
separatorBuilder: (context, _) => const SectionDivider(),
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: addressListViewModel.items.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final item = addressListViewModel.items[index];
|
||||||
|
Widget cell = Container();
|
||||||
|
|
||||||
|
if (item is WalletAccountListHeader) {
|
||||||
|
cell = HeaderTile(
|
||||||
|
onTap: () async => await showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => getIt.get<MoneroAccountListPage>()),
|
||||||
|
title: S.of(context).accounts,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 14,
|
||||||
|
color: Theme.of(context).textTheme!.headline4!.color!,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item is WalletAddressListHeader) {
|
||||||
|
cell = HeaderTile(
|
||||||
|
onTap: () =>
|
||||||
|
Navigator.of(context).pushNamed(Routes.newSubaddress),
|
||||||
|
title: S.of(context).addresses,
|
||||||
|
icon: Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 20,
|
||||||
|
color: Theme.of(context).textTheme!.headline4!.color!,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item is WalletAddressListItem) {
|
||||||
|
cell = Observer(builder: (_) {
|
||||||
|
final isCurrent =
|
||||||
|
item.address == addressListViewModel.address.address;
|
||||||
|
final backgroundColor = isCurrent
|
||||||
|
? Theme.of(context).textTheme!.headline2!.decorationColor!
|
||||||
|
: Theme.of(context).textTheme!.headline3!.decorationColor!;
|
||||||
|
final textColor = isCurrent
|
||||||
|
? Theme.of(context).textTheme!.headline2!.color!
|
||||||
|
: Theme.of(context).textTheme!.headline3!.color!;
|
||||||
|
|
||||||
|
return AddressCell.fromItem(item,
|
||||||
|
isCurrent: isCurrent,
|
||||||
|
backgroundColor: backgroundColor,
|
||||||
|
textColor: textColor,
|
||||||
|
onTap: (_) => addressListViewModel.setAddress(item),
|
||||||
|
onEdit: () => Navigator.of(context)
|
||||||
|
.pushNamed(Routes.newSubaddress, arguments: item));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return index != 0
|
||||||
|
? cell
|
||||||
|
: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(30),
|
||||||
|
topRight: Radius.circular(30)),
|
||||||
|
child: cell,
|
||||||
|
);
|
||||||
|
})),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Observer(
|
))
|
||||||
builder: (_) => ListView.separated(
|
: Padding(
|
||||||
padding: EdgeInsets.all(0),
|
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
||||||
separatorBuilder: (context, _) => const SectionDivider(),
|
child: Column(
|
||||||
shrinkWrap: true,
|
children: [
|
||||||
physics: NeverScrollableScrollPhysics(),
|
Expanded(
|
||||||
itemCount: addressListViewModel.items.length,
|
flex: 7,
|
||||||
itemBuilder: (context, index) {
|
child: QRWidget(
|
||||||
final item = addressListViewModel.items[index];
|
formKey: _formKey,
|
||||||
Widget cell = Container();
|
heroTag: _heroTag,
|
||||||
|
addressListViewModel: addressListViewModel,
|
||||||
if (item is WalletAccountListHeader) {
|
amountTextFieldFocusNode: _cryptoAmountFocus,
|
||||||
cell = HeaderTile(
|
amountController: _amountController,
|
||||||
onTap: () async => await showPopUp<void>(
|
isLight: currentTheme.type == ThemeType.light),
|
||||||
context: context,
|
),
|
||||||
builder: (_) =>
|
Expanded(
|
||||||
getIt.get<MoneroAccountListPage>()),
|
flex: 2,
|
||||||
title: S.of(context).accounts,
|
child: SizedBox(),
|
||||||
icon: Icon(
|
),
|
||||||
Icons.arrow_forward_ios,
|
Text(S.of(context).electrum_address_disclaimer,
|
||||||
size: 14,
|
textAlign: TextAlign.center,
|
||||||
color:
|
style: TextStyle(
|
||||||
Theme.of(context).textTheme!.headline4!.color!,
|
fontSize: 15,
|
||||||
));
|
color: Theme.of(context).accentTextTheme!.headline3!.backgroundColor!)),
|
||||||
}
|
],
|
||||||
|
),
|
||||||
if (item is WalletAddressListHeader) {
|
);
|
||||||
cell = HeaderTile(
|
|
||||||
onTap: () => Navigator.of(context)
|
|
||||||
.pushNamed(Routes.newSubaddress),
|
|
||||||
title: S.of(context).addresses,
|
|
||||||
icon: Icon(
|
|
||||||
Icons.add,
|
|
||||||
size: 20,
|
|
||||||
color:
|
|
||||||
Theme.of(context).textTheme!.headline4!.color!,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item is WalletAddressListItem) {
|
|
||||||
cell = Observer(builder: (_) {
|
|
||||||
final isCurrent = item.address ==
|
|
||||||
addressListViewModel.address.address;
|
|
||||||
final backgroundColor = isCurrent
|
|
||||||
? Theme.of(context)
|
|
||||||
.textTheme!
|
|
||||||
.headline2!
|
|
||||||
.decorationColor!
|
|
||||||
: Theme.of(context)
|
|
||||||
.textTheme!
|
|
||||||
.headline3!
|
|
||||||
.decorationColor!;
|
|
||||||
final textColor = isCurrent
|
|
||||||
? Theme.of(context).textTheme!.headline2!.color!
|
|
||||||
: Theme.of(context).textTheme!.headline3!.color!;
|
|
||||||
|
|
||||||
return AddressCell.fromItem(item,
|
|
||||||
isCurrent: isCurrent,
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
textColor: textColor,
|
|
||||||
onTap: (_) => addressListViewModel.setAddress(item),
|
|
||||||
onEdit: () => Navigator.of(context).pushNamed(
|
|
||||||
Routes.newSubaddress,
|
|
||||||
arguments: item));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return index != 0
|
|
||||||
? cell
|
|
||||||
: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(30),
|
|
||||||
topRight: Radius.circular(30)),
|
|
||||||
child: cell,
|
|
||||||
);
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)) : Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
flex: 7,
|
|
||||||
child: QRWidget(
|
|
||||||
addressListViewModel: addressListViewModel,
|
|
||||||
isAmountFieldShow: true,
|
|
||||||
amountTextFieldFocusNode: _cryptoAmountFocus,
|
|
||||||
isLight: currentTheme.type == ThemeType.light),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: SizedBox(),
|
|
||||||
),
|
|
||||||
Text(S.of(context).electrum_address_disclaimer,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 15,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.accentTextTheme!
|
|
||||||
.headline3!
|
|
||||||
.backgroundColor!)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
120
lib/src/screens/receive/widgets/currency_input_field.dart
Normal file
120
lib/src/screens/receive/widgets/currency_input_field.dart
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import 'package:cake_wallet/core/amount_validator.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||||
|
import 'package:cw_core/currency.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
class CurrencyInputField extends StatelessWidget {
|
||||||
|
const CurrencyInputField({
|
||||||
|
super.key,
|
||||||
|
required this.onTapPicker,
|
||||||
|
required this.selectedCurrency,
|
||||||
|
this.focusNode,
|
||||||
|
required this.controller,
|
||||||
|
});
|
||||||
|
final Function() onTapPicker;
|
||||||
|
final Currency selectedCurrency;
|
||||||
|
final FocusNode? focusNode;
|
||||||
|
final TextEditingController controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final arrowBottomPurple = Image.asset(
|
||||||
|
'assets/images/arrow_bottom_purple_icon.png',
|
||||||
|
color: Colors.white,
|
||||||
|
height: 8,
|
||||||
|
);
|
||||||
|
final _width = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 20),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 40,
|
||||||
|
child: BaseTextFormField(
|
||||||
|
focusNode: focusNode,
|
||||||
|
controller: controller,
|
||||||
|
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
|
||||||
|
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+(\.|\,)?\d{0,8}'))],
|
||||||
|
hintText: '0.000',
|
||||||
|
placeholderTextStyle: TextStyle(
|
||||||
|
color: Theme.of(context).primaryTextTheme.headline5!.color!,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
borderColor: Theme.of(context).accentTextTheme.headline6!.backgroundColor!,
|
||||||
|
textColor: Colors.white,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
prefixIcon: Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
left: _width / 4,
|
||||||
|
),
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.only(right: 8),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTapPicker,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(right: 5),
|
||||||
|
child: arrowBottomPurple,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
selectedCurrency.name.toUpperCase(),
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (selectedCurrency.tag != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 3.0),
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).primaryTextTheme.headline4!.color!,
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
selectedCurrency.tag!,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headline4!
|
||||||
|
.decorationColor!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 3.0),
|
||||||
|
child: Text(
|
||||||
|
':',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,37 +1,36 @@
|
||||||
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart';
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:device_display_brightness/device_display_brightness.dart';
|
import 'package:device_display_brightness/device_display_brightness.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
|
||||||
import 'package:cake_wallet/core/amount_validator.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||||
|
|
||||||
class QRWidget extends StatelessWidget {
|
class QRWidget extends StatelessWidget {
|
||||||
QRWidget(
|
QRWidget({
|
||||||
{required this.addressListViewModel,
|
required this.addressListViewModel,
|
||||||
required this.isLight,
|
required this.isLight,
|
||||||
this.qrVersion,
|
this.qrVersion,
|
||||||
this.isAmountFieldShow = false,
|
this.heroTag,
|
||||||
this.amountTextFieldFocusNode})
|
required this.amountController,
|
||||||
: amountController = TextEditingController(),
|
required this.formKey,
|
||||||
_formKey = GlobalKey<FormState>() {
|
this.amountTextFieldFocusNode,
|
||||||
amountController.addListener(() => addressListViewModel?.amount =
|
});
|
||||||
_formKey.currentState!.validate() ? amountController.text : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
final WalletAddressListViewModel addressListViewModel;
|
final WalletAddressListViewModel addressListViewModel;
|
||||||
final bool isAmountFieldShow;
|
|
||||||
final TextEditingController amountController;
|
final TextEditingController amountController;
|
||||||
final FocusNode? amountTextFieldFocusNode;
|
final FocusNode? amountTextFieldFocusNode;
|
||||||
final GlobalKey<FormState> _formKey;
|
final GlobalKey<FormState> formKey;
|
||||||
final bool isLight;
|
final bool isLight;
|
||||||
final int? qrVersion;
|
final int? qrVersion;
|
||||||
|
final String? heroTag;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -40,7 +39,7 @@ class QRWidget extends StatelessWidget {
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Column(
|
Column(
|
||||||
|
@ -63,18 +62,18 @@ class QRWidget extends StatelessWidget {
|
||||||
flex: 5,
|
flex: 5,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
changeBrightnessForRoute(() async {
|
changeBrightnessForRoute(
|
||||||
await Navigator.pushNamed(
|
() async {
|
||||||
context,
|
await Navigator.pushNamed(context, Routes.fullscreenQR,
|
||||||
Routes.fullscreenQR,
|
arguments: QrViewData(
|
||||||
arguments: {
|
data: addressListViewModel.uri.toString(),
|
||||||
'qrData': addressListViewModel.uri.toString(),
|
heroTag: heroTag,
|
||||||
},
|
));
|
||||||
);
|
},
|
||||||
});
|
);
|
||||||
},
|
},
|
||||||
child: Hero(
|
child: Hero(
|
||||||
tag: Key(addressListViewModel.uri.toString()),
|
tag: Key(heroTag ?? addressListViewModel.uri.toString()),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1.0,
|
aspectRatio: 1.0,
|
||||||
|
@ -83,7 +82,8 @@ class QRWidget extends StatelessWidget {
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
width: 3,
|
width: 3,
|
||||||
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
|
color:
|
||||||
|
Theme.of(context).accentTextTheme.headline2!.backgroundColor!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: QrImage(data: addressListViewModel.uri.toString()),
|
child: QrImage(data: addressListViewModel.uri.toString()),
|
||||||
|
@ -99,77 +99,77 @@ class QRWidget extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (isAmountFieldShow)
|
Observer(builder: (_) {
|
||||||
Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(top: 10),
|
padding: EdgeInsets.only(top: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Form(
|
child: Form(
|
||||||
key: _formKey,
|
key: formKey,
|
||||||
child: BaseTextFormField(
|
child: CurrencyInputField(
|
||||||
focusNode: amountTextFieldFocusNode,
|
focusNode: amountTextFieldFocusNode,
|
||||||
controller: amountController,
|
controller: amountController,
|
||||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
onTapPicker: () => _presentPicker(context),
|
||||||
inputFormatters: [FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]'))],
|
selectedCurrency: addressListViewModel.selectedCurrency,
|
||||||
textAlign: TextAlign.center,
|
|
||||||
hintText: S.of(context).receive_amount,
|
|
||||||
textColor: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
|
|
||||||
borderColor: Theme.of(context).textTheme!.headline5!.decorationColor!,
|
|
||||||
validator: AmountValidator(
|
|
||||||
currency: walletTypeToCryptoCurrency(addressListViewModel!.type),
|
|
||||||
isAutovalidate: true),
|
|
||||||
// FIX-ME: Check does it equal to autovalidate: true,
|
|
||||||
autovalidateMode: AutovalidateMode.always,
|
|
||||||
placeholderTextStyle: TextStyle(
|
|
||||||
color: Theme.of(context).hoverColor,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
Padding(
|
}),
|
||||||
padding: EdgeInsets.only(top: 8, bottom: 8),
|
Padding(
|
||||||
child: Builder(
|
padding: EdgeInsets.only(top: 20, bottom: 8),
|
||||||
builder: (context) => Observer(
|
child: Builder(
|
||||||
builder: (context) => GestureDetector(
|
builder: (context) => Observer(
|
||||||
onTap: () {
|
builder: (context) => GestureDetector(
|
||||||
Clipboard.setData(ClipboardData(text: addressListViewModel!.address.address));
|
onTap: () {
|
||||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
Clipboard.setData(ClipboardData(text: addressListViewModel.address.address));
|
||||||
},
|
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||||
child: Row(
|
},
|
||||||
mainAxisSize: MainAxisSize.max,
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Expanded(
|
children: <Widget>[
|
||||||
child: Text(
|
Expanded(
|
||||||
addressListViewModel!.address.address,
|
child: Text(
|
||||||
textAlign: TextAlign.center,
|
addressListViewModel.address.address,
|
||||||
style: TextStyle(
|
textAlign: TextAlign.center,
|
||||||
fontSize: 15,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w500,
|
fontSize: 15,
|
||||||
color:
|
fontWeight: FontWeight.w500,
|
||||||
Theme.of(context).accentTextTheme!.headline2!.backgroundColor!),
|
color: Theme.of(context).accentTextTheme.headline2!.backgroundColor!),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
),
|
||||||
padding: EdgeInsets.only(left: 12),
|
Padding(
|
||||||
child: copyImage,
|
padding: EdgeInsets.only(left: 12),
|
||||||
)
|
child: copyImage,
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _presentPicker(BuildContext context) async {
|
||||||
|
await showPopUp<void>(
|
||||||
|
builder: (_) => CurrencyPicker(
|
||||||
|
selectedAtIndex: addressListViewModel.selectedCurrencyIndex,
|
||||||
|
items: addressListViewModel.currencies,
|
||||||
|
hintText: S.of(context).search_currency,
|
||||||
|
onItemSelected: addressListViewModel.selectCurrency,
|
||||||
|
),
|
||||||
|
context: context,
|
||||||
|
);
|
||||||
|
// update amount if currency changed
|
||||||
|
addressListViewModel.changeAmount(amountController.text);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> changeBrightnessForRoute(Future<void> Function() navigation) async {
|
Future<void> changeBrightnessForRoute(Future<void> Function() navigation) async {
|
||||||
// if not mobile, just navigate
|
// if not mobile, just navigate
|
||||||
if (!DeviceInfo.instance.isMobile) {
|
if (!DeviceInfo.instance.isMobile) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:device_display_brightness/device_display_brightness.dart';
|
import 'package:device_display_brightness/device_display_brightness.dart';
|
||||||
|
@ -31,9 +32,7 @@ class WalletKeysPage extends BasePage {
|
||||||
await Navigator.pushNamed(
|
await Navigator.pushNamed(
|
||||||
context,
|
context,
|
||||||
Routes.fullscreenQR,
|
Routes.fullscreenQR,
|
||||||
arguments: {
|
arguments: QrViewData(data: await walletKeysViewModel.url.toString()),
|
||||||
'qrData': (await walletKeysViewModel.url).toString(),
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
// ignore: unawaited_futures
|
// ignore: unawaited_futures
|
||||||
DeviceDisplayBrightness.setBrightness(brightness);
|
DeviceDisplayBrightness.setBrightness(brightness);
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:cw_core/currency.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cake_wallet/utils/list_item.dart';
|
import 'package:cake_wallet/utils/list_item.dart';
|
||||||
|
@ -11,37 +14,30 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
import 'package:cw_core/transaction_history.dart';
|
import 'package:cw_core/transaction_history.dart';
|
||||||
import 'package:cw_core/balance.dart';
|
import 'package:cw_core/balance.dart';
|
||||||
import 'package:cw_core/transaction_info.dart';
|
import 'package:cw_core/transaction_info.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'package:cake_wallet/store/app_store.dart';
|
import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'dart:async';
|
|
||||||
import 'package:cake_wallet/monero/monero.dart';
|
import 'package:cake_wallet/monero/monero.dart';
|
||||||
import 'package:cake_wallet/haven/haven.dart';
|
import 'package:cake_wallet/haven/haven.dart';
|
||||||
|
|
||||||
part 'wallet_address_list_view_model.g.dart';
|
part 'wallet_address_list_view_model.g.dart';
|
||||||
|
|
||||||
class WalletAddressListViewModel = WalletAddressListViewModelBase
|
class WalletAddressListViewModel = WalletAddressListViewModelBase with _$WalletAddressListViewModel;
|
||||||
with _$WalletAddressListViewModel;
|
|
||||||
|
|
||||||
abstract class PaymentURI {
|
abstract class PaymentURI {
|
||||||
PaymentURI({
|
PaymentURI({required this.amount, required this.address});
|
||||||
required this.amount,
|
|
||||||
required this.address});
|
|
||||||
|
|
||||||
final String amount;
|
final String amount;
|
||||||
final String address;
|
final String address;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoneroURI extends PaymentURI {
|
class MoneroURI extends PaymentURI {
|
||||||
MoneroURI({
|
MoneroURI({required String amount, required String address})
|
||||||
required String amount,
|
|
||||||
required String address})
|
|
||||||
: super(amount: amount, address: address);
|
: super(amount: amount, address: address);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var base = 'monero:' + address;
|
var base = 'monero:' + address;
|
||||||
|
|
||||||
if (amount?.isNotEmpty ?? false) {
|
if (amount.isNotEmpty) {
|
||||||
base += '?tx_amount=${amount.replaceAll(',', '.')}';
|
base += '?tx_amount=${amount.replaceAll(',', '.')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,16 +46,14 @@ class MoneroURI extends PaymentURI {
|
||||||
}
|
}
|
||||||
|
|
||||||
class HavenURI extends PaymentURI {
|
class HavenURI extends PaymentURI {
|
||||||
HavenURI({
|
HavenURI({required String amount, required String address})
|
||||||
required String amount,
|
|
||||||
required String address})
|
|
||||||
: super(amount: amount, address: address);
|
: super(amount: amount, address: address);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var base = 'haven:' + address;
|
var base = 'haven:' + address;
|
||||||
|
|
||||||
if (amount?.isNotEmpty ?? false) {
|
if (amount.isNotEmpty) {
|
||||||
base += '?tx_amount=${amount.replaceAll(',', '.')}';
|
base += '?tx_amount=${amount.replaceAll(',', '.')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,16 +62,14 @@ class HavenURI extends PaymentURI {
|
||||||
}
|
}
|
||||||
|
|
||||||
class BitcoinURI extends PaymentURI {
|
class BitcoinURI extends PaymentURI {
|
||||||
BitcoinURI({
|
BitcoinURI({required String amount, required String address})
|
||||||
required String amount,
|
|
||||||
required String address})
|
|
||||||
: super(amount: amount, address: address);
|
: super(amount: amount, address: address);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var base = 'bitcoin:' + address;
|
var base = 'bitcoin:' + address;
|
||||||
|
|
||||||
if (amount?.isNotEmpty ?? false) {
|
if (amount.isNotEmpty) {
|
||||||
base += '?amount=${amount.replaceAll(',', '.')}';
|
base += '?amount=${amount.replaceAll(',', '.')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,16 +78,14 @@ class BitcoinURI extends PaymentURI {
|
||||||
}
|
}
|
||||||
|
|
||||||
class LitecoinURI extends PaymentURI {
|
class LitecoinURI extends PaymentURI {
|
||||||
LitecoinURI({
|
LitecoinURI({required String amount, required String address})
|
||||||
required String amount,
|
|
||||||
required String address})
|
|
||||||
: super(amount: amount, address: address);
|
: super(amount: amount, address: address);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
var base = 'litecoin:' + address;
|
var base = 'litecoin:' + address;
|
||||||
|
|
||||||
if (amount?.isNotEmpty ?? false) {
|
if (amount.isNotEmpty) {
|
||||||
base += '?amount=${amount.replaceAll(',', '.')}';
|
base += '?amount=${amount.replaceAll(',', '.')}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,24 +96,33 @@ class LitecoinURI extends PaymentURI {
|
||||||
abstract class WalletAddressListViewModelBase with Store {
|
abstract class WalletAddressListViewModelBase with Store {
|
||||||
WalletAddressListViewModelBase({
|
WalletAddressListViewModelBase({
|
||||||
required AppStore appStore,
|
required AppStore appStore,
|
||||||
required this.yatStore
|
required this.yatStore,
|
||||||
}) : _appStore = appStore,
|
required this.fiatConversionStore,
|
||||||
_baseItems = <ListItem>[],
|
}) : _appStore = appStore,
|
||||||
_wallet = appStore.wallet!,
|
_baseItems = <ListItem>[],
|
||||||
hasAccounts = appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
|
_wallet = appStore.wallet!,
|
||||||
amount = '' {
|
selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type),
|
||||||
_onWalletChangeReaction = reaction((_) => _appStore.wallet, (WalletBase<
|
_cryptoNumberFormat = NumberFormat(_cryptoNumberPattern),
|
||||||
Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>?
|
hasAccounts =
|
||||||
wallet) {
|
appStore.wallet!.type == WalletType.monero || appStore.wallet!.type == WalletType.haven,
|
||||||
if (wallet == null) {
|
amount = '' {
|
||||||
return;
|
|
||||||
}
|
|
||||||
_wallet = wallet;
|
|
||||||
hasAccounts = _wallet.type == WalletType.monero;
|
|
||||||
});
|
|
||||||
_init();
|
_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const String _cryptoNumberPattern = '0.00000000';
|
||||||
|
|
||||||
|
final NumberFormat _cryptoNumberFormat;
|
||||||
|
|
||||||
|
final FiatConversionStore fiatConversionStore;
|
||||||
|
|
||||||
|
List<Currency> get currencies => [walletTypeToCryptoCurrency(_wallet.type), ...FiatCurrency.all];
|
||||||
|
|
||||||
|
@observable
|
||||||
|
Currency selectedCurrency;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
int get selectedCurrencyIndex => currencies.indexOf(selectedCurrency);
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
String amount;
|
String amount;
|
||||||
|
|
||||||
|
@ -156,8 +155,9 @@ abstract class WalletAddressListViewModelBase with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
ObservableList<ListItem> get items =>
|
ObservableList<ListItem> get items => ObservableList<ListItem>()
|
||||||
ObservableList<ListItem>()..addAll(_baseItems)..addAll(addressList);
|
..addAll(_baseItems)
|
||||||
|
..addAll(addressList);
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
ObservableList<ListItem> get addressList {
|
ObservableList<ListItem> get addressList {
|
||||||
|
@ -166,10 +166,7 @@ abstract class WalletAddressListViewModelBase with Store {
|
||||||
|
|
||||||
if (wallet.type == WalletType.monero) {
|
if (wallet.type == WalletType.monero) {
|
||||||
final primaryAddress = monero!.getSubaddressList(wallet).subaddresses.first;
|
final primaryAddress = monero!.getSubaddressList(wallet).subaddresses.first;
|
||||||
final addressItems = monero
|
final addressItems = monero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
|
||||||
!.getSubaddressList(wallet)
|
|
||||||
.subaddresses
|
|
||||||
.map((subaddress) {
|
|
||||||
final isPrimary = subaddress == primaryAddress;
|
final isPrimary = subaddress == primaryAddress;
|
||||||
|
|
||||||
return WalletAddressListItem(
|
return WalletAddressListItem(
|
||||||
|
@ -183,10 +180,7 @@ abstract class WalletAddressListViewModelBase with Store {
|
||||||
|
|
||||||
if (wallet.type == WalletType.haven) {
|
if (wallet.type == WalletType.haven) {
|
||||||
final primaryAddress = haven!.getSubaddressList(wallet).subaddresses.first;
|
final primaryAddress = haven!.getSubaddressList(wallet).subaddresses.first;
|
||||||
final addressItems = haven
|
final addressItems = haven!.getSubaddressList(wallet).subaddresses.map((subaddress) {
|
||||||
!.getSubaddressList(wallet)
|
|
||||||
.subaddresses
|
|
||||||
.map((subaddress) {
|
|
||||||
final isPrimary = subaddress == primaryAddress;
|
final isPrimary = subaddress == primaryAddress;
|
||||||
|
|
||||||
return WalletAddressListItem(
|
return WalletAddressListItem(
|
||||||
|
@ -203,8 +197,7 @@ abstract class WalletAddressListViewModelBase with Store {
|
||||||
final bitcoinAddresses = bitcoin!.getAddresses(wallet).map((addr) {
|
final bitcoinAddresses = bitcoin!.getAddresses(wallet).map((addr) {
|
||||||
final isPrimary = addr == primaryAddress;
|
final isPrimary = addr == primaryAddress;
|
||||||
|
|
||||||
return WalletAddressListItem(
|
return WalletAddressListItem(isPrimary: isPrimary, name: null, address: addr);
|
||||||
isPrimary: isPrimary, name: null, address: addr);
|
|
||||||
});
|
});
|
||||||
addressList.addAll(bitcoinAddresses);
|
addressList.addAll(bitcoinAddresses);
|
||||||
}
|
}
|
||||||
|
@ -234,8 +227,7 @@ abstract class WalletAddressListViewModelBase with Store {
|
||||||
bool get hasAddressList => _wallet.type == WalletType.monero || _wallet.type == WalletType.haven;
|
bool get hasAddressList => _wallet.type == WalletType.monero || _wallet.type == WalletType.haven;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
|
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo> _wallet;
|
||||||
_wallet;
|
|
||||||
|
|
||||||
List<ListItem> _baseItems;
|
List<ListItem> _baseItems;
|
||||||
|
|
||||||
|
@ -243,8 +235,6 @@ abstract class WalletAddressListViewModelBase with Store {
|
||||||
|
|
||||||
final YatStore yatStore;
|
final YatStore yatStore;
|
||||||
|
|
||||||
ReactionDisposer? _onWalletChangeReaction;
|
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void setAddress(WalletAddressListItem address) =>
|
void setAddress(WalletAddressListItem address) =>
|
||||||
_wallet.walletAddresses.address = address.address;
|
_wallet.walletAddresses.address = address.address;
|
||||||
|
@ -258,4 +248,31 @@ abstract class WalletAddressListViewModelBase with Store {
|
||||||
|
|
||||||
_baseItems.add(WalletAddressListHeader());
|
_baseItems.add(WalletAddressListHeader());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void selectCurrency(Currency currency) {
|
||||||
|
selectedCurrency = currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void changeAmount(String amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
if (selectedCurrency is FiatCurrency) {
|
||||||
|
_convertAmountToCrypto();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _convertAmountToCrypto() {
|
||||||
|
final cryptoCurrency = walletTypeToCryptoCurrency(_wallet.type);
|
||||||
|
try {
|
||||||
|
final crypto =
|
||||||
|
double.parse(amount.replaceAll(',', '.')) / fiatConversionStore.prices[cryptoCurrency]!;
|
||||||
|
final cryptoAmountTmp = _cryptoNumberFormat.format(crypto);
|
||||||
|
if (amount != cryptoAmountTmp) {
|
||||||
|
amount = cryptoAmountTmp;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
amount = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue