Merge branch '4.1.0' into CAKE-222-fix-fee-picker-on-the-settings-screen

This commit is contained in:
OleksandrSobol 2021-01-08 17:38:57 +02:00
commit 0e721b7795
47 changed files with 1402 additions and 642 deletions

View file

@ -354,7 +354,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 10; CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -494,7 +494,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 10; CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
@ -528,7 +528,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 10; CURRENT_PROJECT_VERSION = 12;
DEVELOPMENT_TEAM = 32J6BB6VUS; DEVELOPMENT_TEAM = 32J6BB6VUS;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (

View file

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:cake_wallet/entities/crypto_amount_format.dart'; import 'package:cake_wallet/entities/crypto_amount_format.dart';
@ -7,10 +9,32 @@ final bitcoinAmountFormat = NumberFormat()
..maximumFractionDigits = bitcoinAmountLength ..maximumFractionDigits = bitcoinAmountLength
..minimumFractionDigits = 1; ..minimumFractionDigits = 1;
String bitcoinAmountToString({int amount}) => String bitcoinAmountToString({int amount}) => bitcoinAmountFormat.format(
bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
double bitcoinAmountToDouble({int amount}) => cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); double bitcoinAmountToDouble({int amount}) =>
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
int doubleToBitcoinAmount(double amount) => int stringDoubleToBitcoinAmount(String amount) {
(amount * bitcoinAmountDivider).toInt(); final splitted = amount.split('');
final dotIndex = amount.indexOf('.');
int result = 0;
for (var i = 0; i < splitted.length; i++) {
try {
if (dotIndex == i) {
continue;
}
final char = splitted[i];
final multiplier = dotIndex < i
? bitcoinAmountDivider ~/ pow(10, (i - dotIndex))
: (bitcoinAmountDivider * pow(10, (dotIndex - i -1))).toInt();
final num = int.parse(char) * multiplier;
result += num;
} catch (_) {}
}
return result;
}

View file

@ -27,10 +27,10 @@ class BitcoinBalance extends Balance {
final int confirmed; final int confirmed;
final int unconfirmed; final int unconfirmed;
int get total => int get total => confirmed + unconfirmed;
confirmed + (unconfirmed < 0 ? unconfirmed * -1 : unconfirmed);
int get availableBalance => confirmed + (unconfirmed < 0 ? unconfirmed : 0); int get availableBalance =>
(confirmed ?? 0) + (unconfirmed < 0 ? unconfirmed : 0);
String get confirmedFormatted => bitcoinAmountToString(amount: confirmed); String get confirmedFormatted => bitcoinAmountToString(amount: confirmed);

View file

@ -4,6 +4,6 @@ class BitcoinTransactionCredentials {
BitcoinTransactionCredentials(this.address, this.amount, this.priority); BitcoinTransactionCredentials(this.address, this.amount, this.priority);
final String address; final String address;
final double amount; final String amount;
TransactionPriority priority; TransactionPriority priority;
} }

View file

@ -47,7 +47,7 @@ class BitcoinTransactionInfo extends TransactionInfo {
final out = vin['tx']['vout'][vout] as Map; final out = vin['tx']['vout'][vout] as Map;
final outAddresses = final outAddresses =
(out['scriptPubKey']['addresses'] as List<Object>)?.toSet(); (out['scriptPubKey']['addresses'] as List<Object>)?.toSet();
inputsAmount += doubleToBitcoinAmount(out['value'] as double ?? 0); inputsAmount += stringDoubleToBitcoinAmount((out['value'] as double ?? 0).toString());
if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) { if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) {
direction = TransactionDirection.outgoing; direction = TransactionDirection.outgoing;
@ -58,7 +58,7 @@ class BitcoinTransactionInfo extends TransactionInfo {
final outAddresses = final outAddresses =
out['scriptPubKey']['addresses'] as List<Object> ?? []; out['scriptPubKey']['addresses'] as List<Object> ?? [];
final ntrs = outAddresses.toSet().intersection(addressesSet); final ntrs = outAddresses.toSet().intersection(addressesSet);
final value = doubleToBitcoinAmount(out['value'] as double ?? 0.0); final value = stringDoubleToBitcoinAmount((out['value'] as double ?? 0.0).toString());
totalOutAmount += value; totalOutAmount += value;
if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) || if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) ||

View file

@ -116,6 +116,19 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
walletInfo: walletInfo); walletInfo: walletInfo);
} }
static int feeAmountForPriority(TransactionPriority priority) {
switch (priority) {
case TransactionPriority.slow:
return 6000;
case TransactionPriority.regular:
return 22080;
case TransactionPriority.fast:
return 24000;
default:
return 0;
}
}
@override @override
final BitcoinTransactionHistory transactionHistory; final BitcoinTransactionHistory transactionHistory;
final String path; final String path;
@ -243,16 +256,20 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
Object credentials) async { Object credentials) async {
final transactionCredentials = credentials as BitcoinTransactionCredentials; final transactionCredentials = credentials as BitcoinTransactionCredentials;
final inputs = <BitcoinUnspent>[]; final inputs = <BitcoinUnspent>[];
final fee = _feeMultiplier(transactionCredentials.priority); final fee = feeAmountForPriority(transactionCredentials.priority);
final amount = transactionCredentials.amount != null final amount = transactionCredentials.amount != null
? doubleToBitcoinAmount(transactionCredentials.amount) ? stringDoubleToBitcoinAmount(transactionCredentials.amount)
: balance.total - fee; : balance.availableBalance - fee;
final totalAmount = amount + fee; final totalAmount = amount + fee;
final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin); final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin);
var leftAmount = totalAmount;
final changeAddress = address; final changeAddress = address;
var leftAmount = totalAmount;
var totalInputAmount = 0; var totalInputAmount = 0;
if (totalAmount > balance.availableBalance) {
throw BitcoinTransactionWrongBalanceException();
}
final unspent = addresses.map((address) => eclient final unspent = addresses.map((address) => eclient
.getListUnspentWithAddress(address.address) .getListUnspentWithAddress(address.address)
.then((unspent) => unspent .then((unspent) => unspent
@ -334,7 +351,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
@override @override
double calculateEstimatedFee(TransactionPriority priority) => double calculateEstimatedFee(TransactionPriority priority) =>
bitcoinAmountToDouble(amount: _feeMultiplier(priority)); bitcoinAmountToDouble(amount: feeAmountForPriority(priority));
@override @override
Future<void> save() async { Future<void> save() async {
@ -386,17 +403,4 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
String _getAddress({@required int index}) => String _getAddress({@required int index}) =>
generateAddress(hd: hd, index: index); generateAddress(hd: hd, index: index);
int _feeMultiplier(TransactionPriority priority) {
switch (priority) {
case TransactionPriority.slow:
return 6000;
case TransactionPriority.regular:
return 22080;
case TransactionPriority.fast:
return 24000;
default:
return 0;
}
}
} }

View file

@ -23,8 +23,7 @@ String jsonrpcparams(List<Object> params) {
String jsonrpc( String jsonrpc(
{String method, List<Object> params, int id, double version = 2.0}) => {String method, List<Object> params, int id, double version = 2.0}) =>
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json '{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
.encode(params)}}\n';
class SocketTask { class SocketTask {
SocketTask({this.completer, this.isSubscription, this.subject}); SocketTask({this.completer, this.isSubscription, this.subject});
@ -75,7 +74,9 @@ class ElectrumClient {
socket.listen((Uint8List event) { socket.listen((Uint8List event) {
try { try {
_handleResponse(utf8.decode(event.toList())); final response =
json.decode(utf8.decode(event.toList())) as Map<String, Object>;
_handleResponse(response);
} on FormatException catch (e) { } on FormatException catch (e) {
final msg = e.message.toLowerCase(); final msg = e.message.toLowerCase();
@ -87,12 +88,33 @@ class ElectrumClient {
unterminatedString += e.source as String; unterminatedString += e.source as String;
} }
if (msg.contains("not a subtype of type")) {
unterminatedString += e.source as String;
return;
}
if (isJSONStringCorrect(unterminatedString)) { if (isJSONStringCorrect(unterminatedString)) {
_handleResponse(unterminatedString); final response =
json.decode(unterminatedString) as Map<String, Object>;
_handleResponse(response);
unterminatedString = null;
}
} on TypeError catch (e) {
if (!e.toString().contains('Map<String, Object>')) {
return;
}
final source = utf8.decode(event.toList());
unterminatedString += source;
if (isJSONStringCorrect(unterminatedString)) {
final response =
json.decode(unterminatedString) as Map<String, Object>;
_handleResponse(response);
unterminatedString = null; unterminatedString = null;
} }
} catch (e) { } catch (e) {
print(e); print(e.toString());
} }
}, onError: (Object error) { }, onError: (Object error) {
print(error.toString()); print(error.toString());
@ -239,7 +261,7 @@ class ElectrumClient {
if (result is String) { if (result is String) {
return result; return result;
} }
print(result);
return ''; return '';
}); });
@ -275,7 +297,8 @@ class ElectrumClient {
params: [scripthash]); params: [scripthash]);
} }
BehaviorSubject<T> subscribe<T>({@required String id, BehaviorSubject<T> subscribe<T>(
{@required String id,
@required String method, @required String method,
List<Object> params = const []}) { List<Object> params = const []}) {
final subscription = BehaviorSubject<T>(); final subscription = BehaviorSubject<T>();
@ -296,7 +319,8 @@ class ElectrumClient {
return completer.future; return completer.future;
} }
Future<dynamic> callWithTimeout({String method, Future<dynamic> callWithTimeout(
{String method,
List<Object> params = const [], List<Object> params = const [],
int timeout = 2000}) async { int timeout = 2000}) async {
final completer = Completer<dynamic>(); final completer = Completer<dynamic>();
@ -325,8 +349,7 @@ class ElectrumClient {
onConnectionStatusChange = null; onConnectionStatusChange = null;
} }
void _regisryTask(int id, Completer completer) => void _regisryTask(int id, Completer completer) => _tasks[id.toString()] =
_tasks[id.toString()] =
SocketTask(completer: completer, isSubscription: false); SocketTask(completer: completer, isSubscription: false);
void _regisrySubscription(String id, BehaviorSubject subject) => void _regisrySubscription(String id, BehaviorSubject subject) =>
@ -371,22 +394,20 @@ class ElectrumClient {
_isConnected = isConnected; _isConnected = isConnected;
} }
void _handleResponse(String response) { void _handleResponse(Map<String, Object> response) {
print('Response: $response'); final method = response['method'];
final jsoned = json.decode(response) as Map<String, Object>; final id = response['id'] as String;
// print(jsoned); final result = response['result'];
final method = jsoned['method'];
final id = jsoned['id'] as String;
final result = jsoned['result'];
if (method is String) { if (method is String) {
_methodHandler(method: method, request: jsoned); _methodHandler(method: method, request: response);
return; return;
} }
_finish(id, result); _finish(id, result);
} }
} }
// FIXME: move me // FIXME: move me
bool isJSONStringCorrect(String source) { bool isJSONStringCorrect(String source) {
try { try {

View file

@ -28,6 +28,7 @@ import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/settings/change_language.dart'; import 'package:cake_wallet/src/screens/settings/change_language.dart';
import 'package:cake_wallet/src/screens/settings/settings.dart'; import 'package:cake_wallet/src/screens/settings/settings.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
@ -55,6 +56,8 @@ import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/rescan_view_model.dart'; import 'package:cake_wallet/view_model/rescan_view_model.dart';
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart'; import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/auth_view_model.dart'; import 'package:cake_wallet/view_model/auth_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
@ -355,7 +358,8 @@ Future setup(
getIt.get<AppStore>().wallet, getIt.get<AppStore>().wallet,
tradesSource, tradesSource,
getIt.get<ExchangeTemplateStore>(), getIt.get<ExchangeTemplateStore>(),
getIt.get<TradesStore>())); getIt.get<TradesStore>(),
getIt.get<AppStore>().settingsStore));
getIt.registerFactory(() => ExchangeTradeViewModel( getIt.registerFactory(() => ExchangeTradeViewModel(
wallet: getIt.get<AppStore>().wallet, wallet: getIt.get<AppStore>().wallet,
@ -415,11 +419,17 @@ Future setup(
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) => getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) =>
WalletRestorePage(getIt.get<WalletRestoreViewModel>(param1: type))); WalletRestorePage(getIt.get<WalletRestoreViewModel>(param1: type)));
getIt.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>
((TransactionInfo transactionInfo, _) => TransactionDetailsViewModel(
transactionInfo: transactionInfo,
transactionDescriptionBox: transactionDescriptionBox,
settingsStore: getIt.get<SettingsStore>()
));
getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>( getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
(TransactionInfo transactionInfo, _) => TransactionDetailsPage( (TransactionInfo transactionInfo, _) => TransactionDetailsPage(
transactionInfo, transactionDetailsViewModel: getIt
getIt.get<SettingsStore>().shouldSaveRecipientAddress, .get<TransactionDetailsViewModel>(param1: transactionInfo)));
transactionDescriptionBox));
getIt.registerFactoryParam<NewWalletTypePage, getIt.registerFactoryParam<NewWalletTypePage,
void Function(BuildContext, WalletType), bool>( void Function(BuildContext, WalletType), bool>(
@ -428,4 +438,10 @@ Future setup(
getIt.registerFactoryParam<PreSeedPage, WalletType, void>( getIt.registerFactoryParam<PreSeedPage, WalletType, void>(
(WalletType type, _) => PreSeedPage(type)); (WalletType type, _) => PreSeedPage(type));
getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
TradeDetailsViewModel(tradeForDetails: trade, trades: tradesSource));
getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) =>
TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade)));
} }

View file

@ -4,7 +4,7 @@ part 'transaction_description.g.dart';
@HiveType(typeId: 2) @HiveType(typeId: 2)
class TransactionDescription extends HiveObject { class TransactionDescription extends HiveObject {
TransactionDescription({this.id, this.recipientAddress}); TransactionDescription({this.id, this.recipientAddress, this.transactionNote});
static const boxName = 'TransactionDescriptions'; static const boxName = 'TransactionDescriptions';
static const boxKey = 'transactionDescriptionsBoxKey'; static const boxKey = 'transactionDescriptionsBoxKey';
@ -14,4 +14,9 @@ class TransactionDescription extends HiveObject {
@HiveField(1) @HiveField(1)
String recipientAddress; String recipientAddress;
@HiveField(2)
String transactionNote;
String get note => transactionNote ?? '';
} }

File diff suppressed because it is too large Load diff

View file

@ -279,7 +279,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.tradeDetails: case Routes.tradeDetails:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
builder: (_) => TradeDetailsPage(settings.arguments as Trade)); builder: (_) =>
getIt.get<TradeDetailsPage>(param1: settings.arguments as Trade));
case Routes.restoreWalletFromSeedDetails: case Routes.restoreWalletFromSeedDetails:
final args = settings.arguments as List; final args = settings.arguments as List;

View file

@ -62,7 +62,8 @@ class ExchangePage extends BasePage {
@override @override
Widget trailing(BuildContext context) => TrailButton( Widget trailing(BuildContext context) => TrailButton(
caption: S.of(context).reset, onPressed: () { caption: S.of(context).reset,
onPressed: () {
_formKey.currentState.reset(); _formKey.currentState.reset();
exchangeViewModel.reset(); exchangeViewModel.reset();
}); });
@ -95,8 +96,8 @@ class ExchangePage extends BasePage {
return KeyboardActions( return KeyboardActions(
config: KeyboardActionsConfig( config: KeyboardActionsConfig(
keyboardActionsPlatform: KeyboardActionsPlatform.IOS, keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
keyboardBarColor: Theme.of(context).accentTextTheme.body2 keyboardBarColor:
.backgroundColor, Theme.of(context).accentTextTheme.body2.backgroundColor,
nextFocus: false, nextFocus: false,
actions: [ actions: [
KeyboardActionsItem( KeyboardActionsItem(
@ -160,6 +161,11 @@ class ExchangePage extends BasePage {
padding: EdgeInsets.fromLTRB(24, 100, 24, 32), padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
child: Observer( child: Observer(
builder: (_) => ExchangeCard( builder: (_) => ExchangeCard(
hasAllAmount: exchangeViewModel.hasAllAmount,
allAmount: exchangeViewModel.hasAllAmount
? () => exchangeViewModel
.calculateDepositAllAmount()
: null,
amountFocusNode: _depositAmountFocus, amountFocusNode: _depositAmountFocus,
key: depositKey, key: depositKey,
title: S.of(context).you_will_send, title: S.of(context).you_will_send,
@ -398,16 +404,21 @@ class ExchangePage extends BasePage {
text: S.of(context).exchange, text: S.of(context).exchange,
onPressed: () { onPressed: () {
if (_formKey.currentState.validate()) { if (_formKey.currentState.validate()) {
if ((exchangeViewModel.depositCurrency == CryptoCurrency.xmr) if ((exchangeViewModel.depositCurrency ==
&&(!(exchangeViewModel.status is SyncedSyncStatus))) { CryptoCurrency.xmr) &&
(!(exchangeViewModel.status
is SyncedSyncStatus))) {
showPopUp<void>( showPopUp<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertWithOneAction( return AlertWithOneAction(
alertTitle: S.of(context).exchange, alertTitle: S.of(context).exchange,
alertContent: S.of(context).exchange_sync_alert_content, alertContent: S
.of(context)
.exchange_sync_alert_content,
buttonText: S.of(context).ok, buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()); buttonAction: () =>
Navigator.of(context).pop());
}); });
} else { } else {
exchangeViewModel.createTrade(); exchangeViewModel.createTrade();
@ -416,8 +427,8 @@ class ExchangePage extends BasePage {
}, },
color: Theme.of(context).accentTextTheme.body2.color, color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white, textColor: Colors.white,
isLoading: exchangeViewModel.tradeState isLoading:
is TradeIsCreating)), exchangeViewModel.tradeState is TradeIsCreating)),
]), ]),
)), )),
)); ));

View file

@ -27,7 +27,9 @@ class ExchangeCard extends StatefulWidget {
this.borderColor = Colors.transparent, this.borderColor = Colors.transparent,
this.currencyValueValidator, this.currencyValueValidator,
this.addressTextFieldValidator, this.addressTextFieldValidator,
this.amountFocusNode}) this.amountFocusNode,
this.hasAllAmount = false,
this.allAmount})
: super(key: key); : super(key: key);
final List<CryptoCurrency> currencies; final List<CryptoCurrency> currencies;
@ -47,6 +49,8 @@ class ExchangeCard extends StatefulWidget {
final FormFieldValidator<String> currencyValueValidator; final FormFieldValidator<String> currencyValueValidator;
final FormFieldValidator<String> addressTextFieldValidator; final FormFieldValidator<String> addressTextFieldValidator;
final FocusNode amountFocusNode; final FocusNode amountFocusNode;
final bool hasAllAmount;
Function allAmount;
@override @override
ExchangeCardState createState() => ExchangeCardState(); ExchangeCardState createState() => ExchangeCardState();
@ -197,7 +201,36 @@ class ExchangeCardState extends State<ExchangeCard> {
]), ]),
), ),
), ),
) ),
if (widget.hasAllAmount)
Positioned(
top: 5,
right: 55,
child: Container(
height: 32,
width: 32,
margin: EdgeInsets.only(left: 14, top: 4, bottom: 10),
decoration: BoxDecoration(
color: Theme.of(context)
.primaryTextTheme
.display1
.color,
borderRadius: BorderRadius.all(Radius.circular(6))),
child: InkWell(
onTap: () => widget.allAmount?.call(),
child: Center(
child: Text(S.of(context).all,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme
.display1
.decorationColor)),
),
),
))
], ],
)), )),
Padding( Padding(
@ -238,8 +271,7 @@ class ExchangeCardState extends State<ExchangeCard> {
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: color: Theme.of(context)
Theme.of(context)
.accentTextTheme .accentTextTheme
.display4 .display4
.decorationColor), .decorationColor),
@ -251,7 +283,8 @@ class ExchangeCardState extends State<ExchangeCard> {
child: AddressTextField( child: AddressTextField(
controller: addressController, controller: addressController,
placeholder: widget.hasRefundAddress placeholder: widget.hasRefundAddress
? S.of(context).refund_address : null, ? S.of(context).refund_address
: null,
options: [ options: [
AddressTextFieldOption.paste, AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode, AddressTextFieldOption.qrCode,
@ -265,8 +298,7 @@ class ExchangeCardState extends State<ExchangeCard> {
hintStyle: TextStyle( hintStyle: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: color: Theme.of(context)
Theme.of(context)
.accentTextTheme .accentTextTheme
.display4 .display4
.decorationColor), .decorationColor),
@ -281,8 +313,8 @@ class ExchangeCardState extends State<ExchangeCard> {
onTap: () { onTap: () {
Clipboard.setData( Clipboard.setData(
ClipboardData(text: addressController.text)); ClipboardData(text: addressController.text));
showBar<void>(context, showBar<void>(
S.of(context).copied_to_clipboard); context, S.of(context).copied_to_clipboard);
}, },
child: Row( child: Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,

View file

@ -6,8 +6,6 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'; import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart'; import 'package:cake_wallet/src/widgets/standart_list_row.dart';
@ -172,36 +170,14 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
final item = widget.exchangeTradeViewModel.items[index]; final item = widget.exchangeTradeViewModel.items[index];
String value; final value = item.data ?? fetchingLabel;
final content = Observer(builder: (_) { final content = StandartListRow(
switch (index) {
case 0:
value =
'${widget.exchangeTradeViewModel.trade.id ?? fetchingLabel}';
break;
case 1:
value =
'${widget.exchangeTradeViewModel.trade.amount ?? fetchingLabel}';
break;
case 2:
value =
'${widget.exchangeTradeViewModel.trade.state ?? fetchingLabel}';
break;
case 3:
value = widget.exchangeTradeViewModel.trade
.inputAddress ??
fetchingLabel;
break;
}
return StandartListRow(
title: item.title, title: item.title,
value: value, value: value,
valueFontSize: 14, valueFontSize: 14,
image: item.isCopied ? copyImage : null, image: item.isCopied ? copyImage : null,
); );
});
return item.isCopied return item.isCopied
? Builder( ? Builder(
@ -382,7 +358,16 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
}); });
}); });
}, },
actionLeftButton: () => Navigator.of(context).pop()); actionLeftButton: () => Navigator.of(context).pop(),
feeFiatAmount: widget.exchangeTradeViewModel.sendViewModel
.pendingTransaction.feeFormatted,
fiatAmountValue: widget.exchangeTradeViewModel.sendViewModel
.pendingTransactionFeeFiatAmount +
' ' +
widget.exchangeTradeViewModel.sendViewModel.fiat.title,
recipientTitle: S.of(context).recipient_address,
recipientAddress:
widget.exchangeTradeViewModel.sendViewModel.address);
}); });
}); });
} }

View file

@ -49,7 +49,7 @@ class PreSeedPage extends BasePage {
Padding( Padding(
padding: EdgeInsets.only(top: 70, left: 16, right: 16), padding: EdgeInsets.only(top: 70, left: 16, right: 16),
child: Text( child: Text(
S.of(context).pre_seed_description(wordsCount), S.of(context).pre_seed_description(wordsCount.toString()),
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,

View file

@ -31,6 +31,7 @@ class SendPage extends BasePage {
: _addressController = TextEditingController(), : _addressController = TextEditingController(),
_cryptoAmountController = TextEditingController(), _cryptoAmountController = TextEditingController(),
_fiatAmountController = TextEditingController(), _fiatAmountController = TextEditingController(),
_noteController = TextEditingController(),
_formKey = GlobalKey<FormState>(), _formKey = GlobalKey<FormState>(),
_cryptoAmountFocus = FocusNode(), _cryptoAmountFocus = FocusNode(),
_fiatAmountFocus = FocusNode(), _fiatAmountFocus = FocusNode(),
@ -46,6 +47,7 @@ class SendPage extends BasePage {
final TextEditingController _addressController; final TextEditingController _addressController;
final TextEditingController _cryptoAmountController; final TextEditingController _cryptoAmountController;
final TextEditingController _fiatAmountController; final TextEditingController _fiatAmountController;
final TextEditingController _noteController;
final GlobalKey<FormState> _formKey; final GlobalKey<FormState> _formKey;
final FocusNode _cryptoAmountFocus; final FocusNode _cryptoAmountFocus;
final FocusNode _fiatAmountFocus; final FocusNode _fiatAmountFocus;
@ -304,6 +306,30 @@ class SendPage extends BasePage {
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontSize: 14), fontSize: 14),
)), )),
Padding(
padding: EdgeInsets.only(top: 20),
child: BaseTextFormField(
controller: _noteController,
keyboardType: TextInputType.multiline,
maxLines: null,
borderColor: Theme.of(context)
.primaryTextTheme
.headline
.color,
textStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Colors.white),
hintText: S.of(context).note_optional,
placeholderTextStyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.headline
.decorationColor),
),
),
Observer( Observer(
builder: (_) => GestureDetector( builder: (_) => GestureDetector(
onTap: () => onTap: () =>
@ -313,6 +339,7 @@ class SendPage extends BasePage {
child: Row( child: Row(
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment.spaceBetween, MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
S S
@ -326,7 +353,12 @@ class SendPage extends BasePage {
color: Colors.white)), color: Colors.white)),
Container( Container(
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text( Text(
sendViewModel sendViewModel
.estimatedFee .estimatedFee
@ -341,8 +373,30 @@ class SendPage extends BasePage {
//color: Theme.of(context).primaryTextTheme.display2.color, //color: Theme.of(context).primaryTextTheme.display2.color,
color: color:
Colors.white)), Colors.white)),
Padding(
padding:
EdgeInsets.only(top: 5),
child: Text(
sendViewModel
.estimatedFeeFiatAmount
+ ' ' +
sendViewModel
.fiat.title,
style: TextStyle(
fontSize: 12,
fontWeight:
FontWeight.w600,
color: Theme
.of(context)
.primaryTextTheme
.headline
.decorationColor))
),
],
),
Padding( Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
top: 2,
left: 5), left: 5),
child: Icon( child: Icon(
Icons.arrow_forward_ios, Icons.arrow_forward_ios,
@ -534,6 +588,14 @@ class SendPage extends BasePage {
} }
}); });
_noteController.addListener(() {
final note = _noteController.text ?? '';
if (note != sendViewModel.note) {
sendViewModel.note = note;
}
});
reaction((_) => sendViewModel.sendAll, (bool all) { reaction((_) => sendViewModel.sendAll, (bool all) {
if (all) { if (all) {
_cryptoAmountController.text = S.current.all; _cryptoAmountController.text = S.current.all;
@ -571,6 +633,12 @@ class SendPage extends BasePage {
} }
}); });
reaction((_) => sendViewModel.note, (String note) {
if (note != _noteController.text) {
_noteController.text = note;
}
});
reaction((_) => sendViewModel.state, (ExecutionState state) { reaction((_) => sendViewModel.state, (ExecutionState state) {
if (state is FailureState) { if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
@ -596,8 +664,14 @@ class SendPage extends BasePage {
amount: S.of(context).send_amount, amount: S.of(context).send_amount,
amountValue: amountValue:
sendViewModel.pendingTransaction.amountFormatted, sendViewModel.pendingTransaction.amountFormatted,
fiatAmountValue: sendViewModel.pendingTransactionFiatAmount
+ ' ' + sendViewModel.fiat.title,
fee: S.of(context).send_fee, fee: S.of(context).send_fee,
feeValue: sendViewModel.pendingTransaction.feeFormatted, feeValue: sendViewModel.pendingTransaction.feeFormatted,
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmount
+ ' ' + sendViewModel.fiat.title,
recipientTitle: S.of(context).recipient_address,
recipientAddress: sendViewModel.address,
rightButtonText: S.of(context).ok, rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel, leftButtonText: S.of(context).cancel,
actionRightButton: () { actionRightButton: () {
@ -614,96 +688,17 @@ class SendPage extends BasePage {
} }
if (state is TransactionCommitted) { if (state is TransactionCommitted) {
return Stack( return AlertWithOneAction(
children: <Widget>[ alertTitle: '',
Container( alertContent: S.of(context).send_success(
color: Theme.of(context).backgroundColor,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
Center(
child: Padding(
padding: EdgeInsets.only(
top: 220, left: 24, right: 24),
child: Text(
S.of(context).send_success(
sendViewModel.currency sendViewModel.currency
.toString()), .toString()),
textAlign: TextAlign.center, buttonText: S.of(context).ok,
style: TextStyle( buttonAction: () =>
fontSize: 22, Navigator.of(context).pop());
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme
.title
.color,
decoration: TextDecoration.none,
),
),
),
),
Positioned(
left: 24,
right: 24,
bottom: 24,
child: PrimaryButton(
onPressed: () =>
Navigator.of(context).pop(),
text: S.of(context).send_got_it,
color: Theme.of(context)
.accentTextTheme
.body2
.color,
textColor: Colors.white))
],
);
} }
if (state is TransactionCommitting) { return Offstage();
return Stack(
children: <Widget>[
Container(
color: Theme.of(context).backgroundColor,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
BackdropFilter(
filter: ImageFilter.blur(
sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(
color: Theme.of(context)
.backgroundColor
.withOpacity(0.25)),
child: Center(
child: Padding(
padding: EdgeInsets.only(top: 220),
child: Text(
S.of(context).send_sending,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme
.title
.color,
decoration: TextDecoration.none,
),
),
),
),
),
)
],
);
}
return Container();
}); });
}); });
}, },

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/palette.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart'; import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
@ -6,8 +7,12 @@ class ConfirmSendingAlert extends BaseAlertDialog {
@required this.alertTitle, @required this.alertTitle,
@required this.amount, @required this.amount,
@required this.amountValue, @required this.amountValue,
@required this.fiatAmountValue,
@required this.fee, @required this.fee,
@required this.feeValue, @required this.feeValue,
@required this.feeFiatAmount,
@required this.recipientTitle,
@required this.recipientAddress,
@required this.leftButtonText, @required this.leftButtonText,
@required this.rightButtonText, @required this.rightButtonText,
@required this.actionLeftButton, @required this.actionLeftButton,
@ -18,8 +23,12 @@ class ConfirmSendingAlert extends BaseAlertDialog {
final String alertTitle; final String alertTitle;
final String amount; final String amount;
final String amountValue; final String amountValue;
final String fiatAmountValue;
final String fee; final String fee;
final String feeValue; final String feeValue;
final String feeFiatAmount;
final String recipientTitle;
final String recipientAddress;
final String leftButtonText; final String leftButtonText;
final String rightButtonText; final String rightButtonText;
final VoidCallback actionLeftButton; final VoidCallback actionLeftButton;
@ -29,31 +38,50 @@ class ConfirmSendingAlert extends BaseAlertDialog {
@override @override
String get titleText => alertTitle; String get titleText => alertTitle;
@override
bool get isDividerExists => true;
@override @override
String get leftActionButtonText => leftButtonText; String get leftActionButtonText => leftButtonText;
@override @override
String get rightActionButtonText => rightButtonText; String get rightActionButtonText => rightButtonText;
@override @override
VoidCallback get actionLeft => actionLeftButton; VoidCallback get actionLeft => actionLeftButton;
@override @override
VoidCallback get actionRight => actionRightButton; VoidCallback get actionRight => actionRightButton;
@override @override
bool get barrierDismissible => alertBarrierDismissible; bool get barrierDismissible => alertBarrierDismissible;
@override @override
Widget content(BuildContext context) { Widget content(BuildContext context) {
return Column( return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Row( Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
amount, amount,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.normal,
fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
amountValue,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFamily: 'Lato', fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color, color: Theme.of(context).primaryTextTheme.title.color,
@ -61,25 +89,43 @@ class ConfirmSendingAlert extends BaseAlertDialog {
), ),
), ),
Text( Text(
amountValue, fiatAmountValue,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFamily: 'Lato', fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color, color: PaletteDark.pigeonBlue,
decoration: TextDecoration.none, decoration: TextDecoration.none,
), ),
) )
], ],
)
],
), ),
Row( Padding(
padding: EdgeInsets.only(top: 16),
child: Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
fee, fee,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.normal,
fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
feeValue,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFamily: 'Lato', fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color, color: Theme.of(context).primaryTextTheme.title.color,
@ -87,18 +133,52 @@ class ConfirmSendingAlert extends BaseAlertDialog {
), ),
), ),
Text( Text(
feeValue, feeFiatAmount,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 12,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontFamily: 'Lato', fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color, color: PaletteDark.pigeonBlue,
decoration: TextDecoration.none, decoration: TextDecoration.none,
), ),
) )
], ],
) )
], ],
)
),
Padding(
padding: EdgeInsets.fromLTRB(8, 16, 8, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
recipientTitle,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
fontFamily: 'Lato',
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
),
Padding(
padding: EdgeInsets.only(top: 8),
child: Text(
recipientAddress,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: 'Lato',
color: PaletteDark.pigeonBlue,
decoration: TextDecoration.none,
),
)
)
],
),
)
],
); );
} }
} }

View file

@ -1,82 +1,43 @@
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.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/exchange/trade.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/utils/date_formatter.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart'; import 'package:cake_wallet/src/widgets/standart_list_row.dart';
class TradeDetailsPage extends BasePage { class TradeDetailsPage extends BasePage {
TradeDetailsPage(this.trade) : _items = [] { TradeDetailsPage(this.tradeDetailsViewModel);
final dateFormat = DateFormatter.withCurrentLocal();
_items.addAll([
StandartListItem(title: S.current.trade_details_id, value: trade.id),
StandartListItem(
title: S.current.trade_details_state,
value: trade.state != null
? trade.state.toString()
: S.current.trade_details_fetching)
]);
if (trade.provider != null) {
_items.add(StandartListItem(
title: S.current.trade_details_provider,
value: trade.provider.toString()));
}
if (trade.createdAt != null) {
_items.add(StandartListItem(
title: S.current.trade_details_created_at,
value: dateFormat.format(trade.createdAt).toString()));
}
if (trade.from != null && trade.to != null) {
_items.add(StandartListItem(
title: S.current.trade_details_pair,
value: '${trade.from.toString()}${trade.to.toString()}'));
}
}
@override @override
String get title => S.current.trade_details_title; String get title => S.current.trade_details_title;
final Trade trade; final TradeDetailsViewModel tradeDetailsViewModel;
final List<StandartListItem> _items;
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
return Container(child: Observer(builder: (_) { return Observer(builder: (_) {
return ListView.separated( return SectionStandardList(
separatorBuilder: (_, __) => Container( sectionCount: 1,
height: 1, itemCounter: (int _) => tradeDetailsViewModel.items.length,
padding: EdgeInsets.only(left: 24), itemBuilder: (_, __, index) {
color: Theme.of(context).backgroundColor, final item = tradeDetailsViewModel.items[index];
child: Container(
height: 1,
color: Theme.of(context)
.primaryTextTheme
.title
.backgroundColor)),
itemCount: _items.length,
itemBuilder: (BuildContext context, int index) {
final item = _items[index];
final isDrawBottom = index == _items.length - 1 ? true : false;
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Clipboard.setData(ClipboardData(text: '${item.value}')); Clipboard.setData(ClipboardData(text: '${item.value}'));
showBar<void>(context, S.of(context).copied_to_clipboard); showBar<void>(context, S
.of(context)
.copied_to_clipboard);
}, },
child: StandartListRow( child: StandartListRow(
title: '${item.title}', title: '${item.title}',
value: '${item.value}', value: '${item.value}'
isDrawBottom: isDrawBottom,
)); ));
}); });
})); });
} }
} }

View file

@ -1,6 +1,6 @@
class StandartListItem { import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
StandartListItem({this.title, this.value});
final String title; class StandartListItem extends TransactionDetailsListItem {
final String value; StandartListItem({String title, String value})
: super(title: title, value: value);
} }

View file

@ -0,0 +1,8 @@
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
class TextFieldListItem extends TransactionDetailsListItem {
TextFieldListItem({String title, String value, this.onSubmitted})
: super(title: title, value: value);
final Function(String value) onSubmitted;
}

View file

@ -0,0 +1,6 @@
abstract class TransactionDetailsListItem {
TransactionDetailsListItem({this.title, this.value});
final String title;
final String value;
}

View file

@ -1,107 +1,32 @@
import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/show_bar.dart'; import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
import 'package:cake_wallet/monero/monero_transaction_info.dart';
import 'package:cake_wallet/entities/transaction_info.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart'; import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/utils/date_formatter.dart';
import 'package:hive/hive.dart';
class TransactionDetailsPage extends BasePage { class TransactionDetailsPage extends BasePage {
TransactionDetailsPage(this.transactionInfo, bool showRecipientAddress, TransactionDetailsPage({this.transactionDetailsViewModel});
Box<TransactionDescription> transactionDescriptionBox)
: _items = [] {
final dateFormat = DateFormatter.withCurrentLocal();
final tx = transactionInfo;
if (tx is MoneroTransactionInfo) {
final items = [
StandartListItem(
title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date)),
StandartListItem(
title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted()),
StandartListItem(title: S.current.send_fee, value: tx.feeFormatted())
];
if (tx.key?.isNotEmpty ?? null) {
// FIXME: add translation
items.add(StandartListItem(title: 'Transaction Key', value: tx.key));
}
_items.addAll(items);
}
if (tx is BitcoinTransactionInfo) {
final items = [
StandartListItem(
title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date)),
StandartListItem(
title: 'Confirmations', value: tx.confirmations?.toString()),
StandartListItem(
title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted()),
if (tx.feeFormatted()?.isNotEmpty)
StandartListItem(title: S.current.send_fee, value: tx.feeFormatted())
];
_items.addAll(items);
}
if (showRecipientAddress) {
final recipientAddress = transactionDescriptionBox.values
.firstWhere((val) => val.id == transactionInfo.id, orElse: () => null)
?.recipientAddress;
if (recipientAddress?.isNotEmpty ?? false) {
_items.add(StandartListItem(
title: S.current.transaction_details_recipient_address,
value: recipientAddress));
}
}
}
@override @override
String get title => S.current.transaction_details_title; String get title => S.current.transaction_details_title;
final TransactionInfo transactionInfo; final TransactionDetailsViewModel transactionDetailsViewModel;
final List<StandartListItem> _items;
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
return Container( return SectionStandardList(
child: ListView.separated( sectionCount: 1,
separatorBuilder: (context, index) => Container( itemCounter: (int _) => transactionDetailsViewModel.items.length,
height: 1, itemBuilder: (_, __, index) {
padding: EdgeInsets.only(left: 24), final item = transactionDetailsViewModel.items[index];
color: Theme.of(context).backgroundColor,
child: Container(
height: 1,
color:
Theme.of(context).primaryTextTheme.title.backgroundColor,
),
),
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
final isDrawBottom = index == _items.length - 1 ? true : false;
if (item is StandartListItem) {
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
Clipboard.setData(ClipboardData(text: item.value)); Clipboard.setData(ClipboardData(text: item.value));
@ -110,10 +35,19 @@ class TransactionDetailsPage extends BasePage {
}, },
child: StandartListRow( child: StandartListRow(
title: '${item.title}:', title: '${item.title}:',
value: item.value, value: item.value),
isDrawBottom: isDrawBottom),
);
}),
); );
} }
if (item is TextFieldListItem) {
return TextFieldListRow(
title: item.title,
value: item.value,
onSubmitted: item.onSubmitted,
);
}
return null;
});
}
} }

View file

@ -0,0 +1,71 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class TextFieldListRow extends StatelessWidget {
TextFieldListRow(
{this.title,
this.value,
this.titleFontSize = 14,
this.valueFontSize = 16,
this.onSubmitted}) {
_textController = TextEditingController();
_textController.text = value;
}
final String title;
final String value;
final double titleFontSize;
final double valueFontSize;
final Function(String value) onSubmitted;
TextEditingController _textController;
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Theme.of(context).backgroundColor,
child: Padding(
padding:
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title,
style: TextStyle(
fontSize: titleFontSize,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme.overline.color),
textAlign: TextAlign.left),
TextField(
controller: _textController,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.done,
maxLines: null,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: valueFontSize,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme.title.color),
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.only(top: 12, bottom: 0),
hintText: S.of(context).enter_your_note,
hintStyle: TextStyle(
fontSize: valueFontSize,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme.overline.color),
border: InputBorder.none
),
onSubmitted: (value) => onSubmitted.call(value),
)
]),
),
);
}
}

View file

@ -7,6 +7,7 @@ class BaseAlertDialog extends StatelessWidget {
String get contentText => ''; String get contentText => '';
String get leftActionButtonText => ''; String get leftActionButtonText => '';
String get rightActionButtonText => ''; String get rightActionButtonText => '';
bool get isDividerExists => false;
VoidCallback get actionLeft => () {}; VoidCallback get actionLeft => () {};
VoidCallback get actionRight => () {}; VoidCallback get actionRight => () {};
bool get barrierDismissible => true; bool get barrierDismissible => true;
@ -127,19 +128,28 @@ class BaseAlertDialog extends StatelessWidget {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
Container( Column(
padding: EdgeInsets.fromLTRB(24, 32, 24, 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
title(context),
Padding( Padding(
padding: EdgeInsets.only(top: 8), padding: EdgeInsets.fromLTRB(24, 32, 24, 0),
child: title(context),
),
isDividerExists
? Padding(
padding: EdgeInsets.only(top: 16, bottom: 8),
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
)
: Offstage(),
Padding(
padding: EdgeInsets.fromLTRB(24, 8, 24, 32),
child: content(context), child: content(context),
) )
], ],
), ),
),
Container( Container(
height: 1, height: 1,
color: Theme.of(context).dividerColor, color: Theme.of(context).dividerColor,

View file

@ -51,7 +51,7 @@ class BaseTextFormField extends StatelessWidget {
final FocusNode focusNode; final FocusNode focusNode;
final bool readOnly; final bool readOnly;
final bool enableInteractiveSelection; final bool enableInteractiveSelection;
String initialValue; final String initialValue;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View file

@ -7,21 +7,17 @@ class StandartListRow extends StatelessWidget {
this.value, this.value,
this.titleFontSize = 14, this.titleFontSize = 14,
this.valueFontSize = 16, this.valueFontSize = 16,
this.image, this.image});
this.isDrawBottom = false});
final String title; final String title;
final String value; final String value;
final double titleFontSize; final double titleFontSize;
final double valueFontSize; final double valueFontSize;
final Image image; final Image image;
final bool isDrawBottom;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Container(
children: <Widget>[
Container(
width: double.infinity, width: double.infinity,
color: Theme.of(context).backgroundColor, color: Theme.of(context).backgroundColor,
child: Padding( child: Padding(
@ -65,19 +61,6 @@ class StandartListRow extends StatelessWidget {
) )
]), ]),
), ),
),
isDrawBottom
? Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).primaryTextTheme.title.backgroundColor,
),
)
: Offstage(),
],
); );
} }
} }

View file

@ -85,6 +85,11 @@ abstract class SettingsStoreBase with Store {
(String languageCode) => sharedPreferences.setString( (String languageCode) => sharedPreferences.setString(
PreferencesKey.currentLanguageCode, languageCode)); PreferencesKey.currentLanguageCode, languageCode));
reaction((_) => balanceDisplayMode,
(BalanceDisplayMode mode) => sharedPreferences.setInt(
PreferencesKey.currentBalanceDisplayModeKey,
mode.serialize()));
this this
.nodes .nodes
.observe((change) => _saveCurrentNode(change.newValue, change.key)); .observe((change) => _saveCurrentNode(change.newValue, change.key));
@ -153,7 +158,7 @@ abstract class SettingsStoreBase with Store {
.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
false; false;
final legacyTheme = final legacyTheme =
sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false)
? ThemeType.dark.index ? ThemeType.dark.index
: ThemeType.bright.index; : ThemeType.bright.index;
final savedTheme = ThemeList.deserialize( final savedTheme = ThemeList.deserialize(

View file

@ -1,5 +1,9 @@
import 'package:cake_wallet/bitcoin/bitcoin_balance.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/entities/balance.dart';
import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/monero/monero_balance.dart';
import 'package:cake_wallet/monero/monero_wallet.dart'; import 'package:cake_wallet/monero/monero_wallet.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
@ -18,8 +22,30 @@ abstract class BalanceViewModelBase with Store {
BalanceViewModelBase( BalanceViewModelBase(
{@required this.appStore, {@required this.appStore,
@required this.settingsStore, @required this.settingsStore,
@required this.fiatConvertationStore}) @required this.fiatConvertationStore}) {
: isReversing = false; isReversing = false;
wallet ??= appStore.wallet;
_reaction = reaction((_) => appStore.wallet, _onWalletChange);
final _wallet = wallet;
if (_wallet is MoneroWallet) {
balance = _wallet.balance;
}
if (_wallet is BitcoinWallet) {
balance = _wallet.balance;
}
_onCurrentWalletChangeReaction =
reaction<void>((_) => wallet.balance, (dynamic balance) {
if (balance is Balance) {
this.balance = balance;
}
});
}
final AppStore appStore; final AppStore appStore;
final SettingsStore settingsStore; final SettingsStore settingsStore;
@ -31,6 +57,12 @@ abstract class BalanceViewModelBase with Store {
@observable @observable
bool isReversing; bool isReversing;
@observable
Balance balance;
@observable
WalletBase wallet;
@computed @computed
double get price => fiatConvertationStore.prices[appStore.wallet.currency]; double get price => fiatConvertationStore.prices[appStore.wallet.currency];
@ -47,24 +79,24 @@ abstract class BalanceViewModelBase with Store {
@computed @computed
String get cryptoBalance { String get cryptoBalance {
final walletBalance = _walletBalance; final walletBalance = _walletBalance;
var balance = '---'; var _balance = '---';
if (displayMode == BalanceDisplayMode.availableBalance) { if (displayMode == BalanceDisplayMode.availableBalance) {
balance = walletBalance.unlockedBalance ?? '0.0'; _balance = walletBalance.unlockedBalance ?? '0.0';
} }
if (displayMode == BalanceDisplayMode.fullBalance) { if (displayMode == BalanceDisplayMode.fullBalance) {
balance = walletBalance.totalBalance ?? '0.0'; _balance = walletBalance.totalBalance ?? '0.0';
} }
return balance; return _balance;
} }
@computed @computed
String get fiatBalance { String get fiatBalance {
final walletBalance = _walletBalance; final walletBalance = _walletBalance;
final fiatCurrency = settingsStore.fiatCurrency; final fiatCurrency = settingsStore.fiatCurrency;
var balance = '---'; var _balance = '---';
final totalBalance = final totalBalance =
_getFiatBalance(price: price, cryptoAmount: walletBalance.totalBalance); _getFiatBalance(price: price, cryptoAmount: walletBalance.totalBalance);
@ -73,30 +105,30 @@ abstract class BalanceViewModelBase with Store {
price: price, cryptoAmount: walletBalance.unlockedBalance); price: price, cryptoAmount: walletBalance.unlockedBalance);
if (displayMode == BalanceDisplayMode.availableBalance) { if (displayMode == BalanceDisplayMode.availableBalance) {
balance = fiatCurrency.toString() + ' ' + unlockedBalance ?? '0.00'; _balance = fiatCurrency.toString() + ' ' + unlockedBalance ?? '0.00';
} }
if (displayMode == BalanceDisplayMode.fullBalance) { if (displayMode == BalanceDisplayMode.fullBalance) {
balance = fiatCurrency.toString() + ' ' + totalBalance ?? '0.00'; _balance = fiatCurrency.toString() + ' ' + totalBalance ?? '0.00';
} }
return balance; return _balance;
} }
@computed @computed
WalletBalance get _walletBalance { WalletBalance get _walletBalance {
final _wallet = appStore.wallet; final _balance = balance;
if (_wallet is MoneroWallet) { if (_balance is MoneroBalance) {
return WalletBalance( return WalletBalance(
unlockedBalance: _wallet.balance.formattedUnlockedBalance, unlockedBalance: _balance.formattedUnlockedBalance,
totalBalance: _wallet.balance.formattedFullBalance); totalBalance: _balance.formattedFullBalance);
} }
if (_wallet is BitcoinWallet) { if (_balance is BitcoinBalance) {
return WalletBalance( return WalletBalance(
unlockedBalance: _wallet.balance.availableBalanceFormatted, unlockedBalance: _balance.availableBalanceFormatted,
totalBalance: _wallet.balance.totalFormatted); totalBalance: _balance.totalFormatted);
} }
return null; return null;
@ -105,6 +137,30 @@ abstract class BalanceViewModelBase with Store {
@computed @computed
CryptoCurrency get currency => appStore.wallet.currency; CryptoCurrency get currency => appStore.wallet.currency;
ReactionDisposer _onCurrentWalletChangeReaction;
ReactionDisposer _reaction;
@action
void _onWalletChange(WalletBase wallet) {
this.wallet = wallet;
if (wallet is MoneroWallet) {
balance = wallet.balance;
}
if (wallet is BitcoinWallet) {
balance = wallet.balance;
}
_onCurrentWalletChangeReaction?.reaction?.dispose();
_onCurrentWalletChangeReaction =
reaction<void>((_) => wallet.balance, (dynamic balance) {
if (balance is Balance) {
this.balance = balance;
}
});
}
String _getFiatBalance({double price, String cryptoAmount}) { String _getFiatBalance({double price, String cryptoAmount}) {
if (cryptoAmount == null) { if (cryptoAmount == null) {
return '0.00'; return '0.00';

View file

@ -231,6 +231,8 @@ abstract class DashboardViewModelBase with Store {
_onMoneroTransactionsUpdate(wallet); _onMoneroTransactionsUpdate(wallet);
} else { } else {
subname = null;
transactions.clear(); transactions.clear();
transactions.addAll(wallet.transactionHistory.transactions.values.map( transactions.addAll(wallet.transactionHistory.transactions.values.map(

View file

@ -39,20 +39,11 @@ abstract class ExchangeTradeViewModelBase with Store {
} }
items = ObservableList<ExchangeTradeItem>(); items = ObservableList<ExchangeTradeItem>();
items.addAll([
ExchangeTradeItem( _updateItems();
title: S.current.id, data: '${trade.id}', isCopied: true),
ExchangeTradeItem(
title: S.current.amount, data: '${trade.amount}', isCopied: false),
ExchangeTradeItem(
title: S.current.status, data: '${trade.state}', isCopied: false),
ExchangeTradeItem(
title: S.current.widgets_address + ':',
data: trade.inputAddress,
isCopied: true),
]);
_updateTrade(); _updateTrade();
_timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade()); _timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
} }
@ -95,8 +86,27 @@ abstract class ExchangeTradeViewModelBase with Store {
} }
trade = updatedTrade; trade = updatedTrade;
_updateItems();
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
} }
} }
void _updateItems() {
items?.clear();
items.addAll([
ExchangeTradeItem(
title: S.current.id, data: '${trade.id}', isCopied: true),
ExchangeTradeItem(
title: S.current.amount, data: '${trade.amount}', isCopied: false),
ExchangeTradeItem(
title: S.current.status, data: '${trade.state}', isCopied: false),
ExchangeTradeItem(
title: S.current.widgets_address + ':',
data: trade.inputAddress,
isCopied: true),
]);
}
} }

View file

@ -1,3 +1,5 @@
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/core/wallet_base.dart';
import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/crypto_currency.dart';
import 'package:cake_wallet/entities/sync_status.dart'; import 'package:cake_wallet/entities/sync_status.dart';
@ -7,6 +9,7 @@ import 'package:cake_wallet/exchange/limits.dart';
import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/limits_state.dart'; import 'package:cake_wallet/exchange/limits_state.dart';
import 'package:cake_wallet/store/dashboard/trades_store.dart'; import 'package:cake_wallet/store/dashboard/trades_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
@ -27,8 +30,8 @@ part 'exchange_view_model.g.dart';
class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
abstract class ExchangeViewModelBase with Store { abstract class ExchangeViewModelBase with Store {
ExchangeViewModelBase( ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore,
this.wallet, this.trades, this._exchangeTemplateStore, this.tradesStore) { this.tradesStore, this._settingsStore) {
providerList = [ providerList = [
XMRTOExchangeProvider(), XMRTOExchangeProvider(),
ChangeNowExchangeProvider(), ChangeNowExchangeProvider(),
@ -104,10 +107,6 @@ abstract class ExchangeViewModelBase with Store {
@observable @observable
bool isReceiveAmountEntered; bool isReceiveAmountEntered;
Limits limits;
NumberFormat _cryptoNumberFormat;
@computed @computed
SyncStatus get status => wallet.syncStatus; SyncStatus get status => wallet.syncStatus;
@ -115,6 +114,15 @@ abstract class ExchangeViewModelBase with Store {
ObservableList<ExchangeTemplate> get templates => ObservableList<ExchangeTemplate> get templates =>
_exchangeTemplateStore.templates; _exchangeTemplateStore.templates;
bool get hasAllAmount =>
wallet.type == WalletType.bitcoin && depositCurrency == wallet.currency;
Limits limits;
NumberFormat _cryptoNumberFormat;
SettingsStore _settingsStore;
@action @action
void changeProvider({ExchangeProvider provider}) { void changeProvider({ExchangeProvider provider}) {
this.provider = provider; this.provider = provider;
@ -264,9 +272,8 @@ abstract class ExchangeViewModelBase with Store {
await trades.add(trade); await trades.add(trade);
tradeState = TradeIsCreatedSuccessfully(trade: trade); tradeState = TradeIsCreatedSuccessfully(trade: trade);
} catch (e) { } catch (e) {
tradeState = TradeIsCreatedFailure( tradeState =
title: provider.title, TradeIsCreatedFailure(title: provider.title, error: e.toString());
error: e.toString());
} }
} }
} else { } else {
@ -291,6 +298,22 @@ abstract class ExchangeViewModelBase with Store {
_onPairChange(); _onPairChange();
} }
@action
void calculateDepositAllAmount() {
if (wallet is BitcoinWallet) {
final availableBalance = wallet.balance.availableBalance as int;
final fee = BitcoinWalletBase.feeAmountForPriority(
_settingsStore.transactionPriority);
if (availableBalance < fee || availableBalance == 0) {
return;
}
final amount = availableBalance - fee;
changeDepositAmount(amount: bitcoinAmountToString(amount: amount));
}
}
void updateTemplate() => _exchangeTemplateStore.update(); void updateTemplate() => _exchangeTemplateStore.update();
void addTemplate( void addTemplate(

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@ -37,6 +38,7 @@ abstract class SendViewModelBase with Store {
this._fiatConversationStore, this.transactionDescriptionBox) this._fiatConversationStore, this.transactionDescriptionBox)
: state = InitialExecutionState(), : state = InitialExecutionState(),
_cryptoNumberFormat = NumberFormat(), _cryptoNumberFormat = NumberFormat(),
note = '',
sendAll = false { sendAll = false {
final _priority = _settingsStore.transactionPriority; final _priority = _settingsStore.transactionPriority;
@ -60,6 +62,9 @@ abstract class SendViewModelBase with Store {
@observable @observable
String address; String address;
@observable
String note;
@observable @observable
bool sendAll; bool sendAll;
@ -67,6 +72,50 @@ abstract class SendViewModelBase with Store {
double get estimatedFee => double get estimatedFee =>
_wallet.calculateEstimatedFee(_settingsStore.transactionPriority); _wallet.calculateEstimatedFee(_settingsStore.transactionPriority);
@computed
String get estimatedFeeFiatAmount {
try {
final fiat = calculateFiatAmountRaw(
price: _fiatConversationStore.prices[_wallet.currency],
cryptoAmount: estimatedFee);
return fiat;
} catch (_) {
return '0.00';
}
}
@computed
String get pendingTransactionFiatAmount {
try {
if (pendingTransaction != null) {
final fiat = calculateFiatAmount(
price: _fiatConversationStore.prices[_wallet.currency],
cryptoAmount: pendingTransaction.amountFormatted);
return fiat;
} else {
return '0.00';
}
} catch (_) {
return '0.00';
}
}
@computed
String get pendingTransactionFeeFiatAmount {
try {
if (pendingTransaction != null) {
final fiat = calculateFiatAmount(
price: _fiatConversationStore.prices[_wallet.currency],
cryptoAmount: pendingTransaction.feeFormatted);
return fiat;
} else {
return '0.00';
}
} catch (_) {
return '0.00';
}
}
FiatCurrency get fiat => _settingsStore.fiatCurrency; FiatCurrency get fiat => _settingsStore.fiatCurrency;
TransactionPriority get transactionPriority => TransactionPriority get transactionPriority =>
@ -112,6 +161,7 @@ abstract class SendViewModelBase with Store {
cryptoAmount = ''; cryptoAmount = '';
fiatAmount = ''; fiatAmount = '';
address = ''; address = '';
note = '';
} }
@action @action
@ -131,10 +181,13 @@ abstract class SendViewModelBase with Store {
state = TransactionCommitting(); state = TransactionCommitting();
await pendingTransaction.commit(); await pendingTransaction.commit();
if (_settingsStore.shouldSaveRecipientAddress && if (pendingTransaction.id?.isNotEmpty ?? false) {
(pendingTransaction.id?.isNotEmpty ?? false)) { _settingsStore.shouldSaveRecipientAddress
await transactionDescriptionBox.add(TransactionDescription( ? await transactionDescriptionBox.add(TransactionDescription(
id: pendingTransaction.id, recipientAddress: address)); id: pendingTransaction.id, recipientAddress: address,
transactionNote: note))
: await transactionDescriptionBox.add(TransactionDescription(
id: pendingTransaction.id, transactionNote: note));
} }
state = TransactionCommitted(); state = TransactionCommitted();
@ -204,7 +257,7 @@ abstract class SendViewModelBase with Store {
switch (_wallet.type) { switch (_wallet.type) {
case WalletType.bitcoin: case WalletType.bitcoin:
final amount = !sendAll ? double.parse(_amount) : null; final amount = !sendAll ? _amount : null;
return BitcoinTransactionCredentials( return BitcoinTransactionCredentials(
address, amount, _settingsStore.transactionPriority); address, amount, _settingsStore.transactionPriority);

View file

@ -0,0 +1,105 @@
import 'dart:async';
import 'package:cake_wallet/exchange/changenow/changenow_exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/morphtoken/morphtoken_exchange_provider.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/utils/date_formatter.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
part 'trade_details_view_model.g.dart';
class TradeDetailsViewModel = TradeDetailsViewModelBase
with _$TradeDetailsViewModel;
abstract class TradeDetailsViewModelBase with Store {
TradeDetailsViewModelBase({Trade tradeForDetails, this.trades}) {
trade = tradeForDetails;
switch (trade.provider) {
case ExchangeProviderDescription.xmrto:
_provider = XMRTOExchangeProvider();
break;
case ExchangeProviderDescription.changeNow:
_provider = ChangeNowExchangeProvider();
break;
case ExchangeProviderDescription.morphToken:
_provider = MorphTokenExchangeProvider(trades: trades);
break;
}
items = ObservableList<StandartListItem>();
_updateItems();
_updateTrade();
_timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
}
final Box<Trade> trades;
@observable
Trade trade;
@observable
ObservableList<StandartListItem> items;
ExchangeProvider _provider;
Timer _timer;
@action
Future<void> _updateTrade() async {
try {
final updatedTrade = await _provider.findTradeById(id: trade.id);
if (updatedTrade.createdAt == null && trade.createdAt != null) {
updatedTrade.createdAt = trade.createdAt;
}
trade = updatedTrade;
_updateItems();
} catch (e) {
print(e.toString());
}
}
void _updateItems() {
final dateFormat = DateFormatter.withCurrentLocal();
items?.clear();
items.addAll([
StandartListItem(title: S.current.trade_details_id, value: trade.id),
StandartListItem(
title: S.current.trade_details_state,
value: trade.state != null
? trade.state.toString()
: S.current.trade_details_fetching)
]);
if (trade.provider != null) {
items.add(StandartListItem(
title: S.current.trade_details_provider,
value: trade.provider.toString()));
}
if (trade.createdAt != null) {
items.add(StandartListItem(
title: S.current.trade_details_created_at,
value: dateFormat.format(trade.createdAt).toString()));
}
if (trade.from != null && trade.to != null) {
items.add(StandartListItem(
title: S.current.trade_details_pair,
value: '${trade.from.toString()}${trade.to.toString()}'));
}
}
}

View file

@ -0,0 +1,114 @@
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
import 'package:cake_wallet/entities/transaction_info.dart';
import 'package:cake_wallet/monero/monero_transaction_info.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
import 'package:cake_wallet/utils/date_formatter.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/generated/i18n.dart';
part 'transaction_details_view_model.g.dart';
class TransactionDetailsViewModel = TransactionDetailsViewModelBase
with _$TransactionDetailsViewModel;
abstract class TransactionDetailsViewModelBase with Store {
TransactionDetailsViewModelBase(
{this.transactionInfo,
this.transactionDescriptionBox,
this.settingsStore})
: items = [] {
showRecipientAddress = settingsStore?.shouldSaveRecipientAddress ?? false;
final dateFormat = DateFormatter.withCurrentLocal();
final tx = transactionInfo;
if (tx is MoneroTransactionInfo) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date)),
StandartListItem(
title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted()),
StandartListItem(
title: S.current.transaction_details_fee, value: tx.feeFormatted()),
];
if (tx.key?.isNotEmpty ?? null) {
_items.add(
StandartListItem(title: S.current.transaction_key, value: tx.key));
}
items.addAll(_items);
}
if (tx is BitcoinTransactionInfo) {
final _items = [
StandartListItem(
title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date)),
StandartListItem(
title: S.current.confirmations,
value: tx.confirmations?.toString()),
StandartListItem(
title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted()),
if (tx.feeFormatted()?.isNotEmpty)
StandartListItem(
title: S.current.transaction_details_fee,
value: tx.feeFormatted())
];
items.addAll(_items);
}
if (showRecipientAddress) {
final recipientAddress = transactionDescriptionBox.values
.firstWhere((val) => val.id == transactionInfo.id, orElse: () => null)
?.recipientAddress;
if (recipientAddress?.isNotEmpty ?? false) {
items.add(StandartListItem(
title: S.current.transaction_details_recipient_address,
value: recipientAddress));
}
}
final description = transactionDescriptionBox.values.firstWhere(
(val) => val.id == transactionInfo.id,
orElse: () => TransactionDescription(id: transactionInfo.id));
items.add(TextFieldListItem(
title: S.current.note_tap_to_change,
value: description.note,
onSubmitted: (value) {
description.transactionNote = value;
if (description.isInBox) {
description.save();
} else {
transactionDescriptionBox.add(description);
}
}));
}
final TransactionInfo transactionInfo;
final Box<TransactionDescription> transactionDescriptionBox;
final SettingsStore settingsStore;
final List<TransactionDetailsListItem> items;
bool showRecipientAddress;
}

View file

@ -11,7 +11,7 @@ description: Cake Wallet.
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 4.1.0+26 version: 4.1.0+28
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.7.0 <3.0.0"

View file

@ -212,7 +212,7 @@
"send_name" : "Name", "send_name" : "Name",
"send_got_it" : "Ich habs", "send_got_it" : "Ich habs",
"send_sending" : "Senden...", "send_sending" : "Senden...",
"send_success" : "Ihr Monero wurde erfolgreich gesendet", "send_success" : "Ihr ${crypto} wurde erfolgreich gesendet",
"settings_title" : "die Einstellungen", "settings_title" : "die Einstellungen",
@ -282,6 +282,7 @@
"transaction_details_date" : "Datum", "transaction_details_date" : "Datum",
"transaction_details_height" : "Höhe", "transaction_details_height" : "Höhe",
"transaction_details_amount" : "Menge", "transaction_details_amount" : "Menge",
"transaction_details_fee" : "Gebühr",
"transaction_details_copied" : "${title} in die Zwischenablage kopiert", "transaction_details_copied" : "${title} in die Zwischenablage kopiert",
"transaction_details_recipient_address" : "Empfängeradresse", "transaction_details_recipient_address" : "Empfängeradresse",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "Bitte warten Sie, bis Ihre Brieftasche synchronisiert ist", "exchange_sync_alert_content" : "Bitte warten Sie, bis Ihre Brieftasche synchronisiert ist",
"pre_seed_title" : "WICHTIG", "pre_seed_title" : "WICHTIG",
"pre_seed_description" : "Auf der nächsten Seite sehen Sie eine Reihe von 25 Wörtern. Dies ist Ihr einzigartiger und privater Samen und der EINZIGE Weg, um Ihren Geldbeutel im Falle eines Verlusts oder einer Fehlfunktion wiederherzustellen. Es liegt in IHRER Verantwortung, es aufzuschreiben und an einem sicheren Ort außerhalb der Cake Wallet App aufzubewahren.", "pre_seed_description" : "Auf der nächsten Seite sehen Sie eine Reihe von ${words} Wörtern. Dies ist Ihr einzigartiger und privater Samen und der EINZIGE Weg, um Ihren Geldbeutel im Falle eines Verlusts oder einer Fehlfunktion wiederherzustellen. Es liegt in IHRER Verantwortung, es aufzuschreiben und an einem sicheren Ort außerhalb der Cake Wallet App aufzubewahren.",
"pre_seed_button_text" : "Ich verstehe. Zeig mir meinen Samen", "pre_seed_button_text" : "Ich verstehe. Zeig mir meinen Samen",
"xmr_to_error" : "XMR.TO-Fehler", "xmr_to_error" : "XMR.TO-Fehler",
@ -428,5 +429,11 @@
"color_theme" : "Farbthema", "color_theme" : "Farbthema",
"light_theme" : "Licht", "light_theme" : "Licht",
"bright_theme" : "Hell", "bright_theme" : "Hell",
"dark_theme" : "Dunkel" "dark_theme" : "Dunkel",
"enter_your_note" : "Geben Sie Ihre Notiz ein…",
"note_optional" : "Hinweis (optional)",
"note_tap_to_change" : "Hinweis (zum Ändern tippen)",
"transaction_key" : "Transaktionsschlüssel",
"confirmations" : "Bestätigungen",
"recipient_address" : "Empfängeradresse"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "Name", "send_name" : "Name",
"send_got_it" : "Got it", "send_got_it" : "Got it",
"send_sending" : "Sending...", "send_sending" : "Sending...",
"send_success" : "Your Monero was successfully sent", "send_success" : "Your ${crypto} was successfully sent",
"settings_title" : "Settings", "settings_title" : "Settings",
@ -282,6 +282,7 @@
"transaction_details_date" : "Date", "transaction_details_date" : "Date",
"transaction_details_height" : "Height", "transaction_details_height" : "Height",
"transaction_details_amount" : "Amount", "transaction_details_amount" : "Amount",
"transaction_details_fee" : "Fee",
"transaction_details_copied" : "${title} copied to Clipboard", "transaction_details_copied" : "${title} copied to Clipboard",
"transaction_details_recipient_address" : "Recipient address", "transaction_details_recipient_address" : "Recipient address",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "Please wait until your wallet is synchronized", "exchange_sync_alert_content" : "Please wait until your wallet is synchronized",
"pre_seed_title" : "IMPORTANT", "pre_seed_title" : "IMPORTANT",
"pre_seed_description" : "On the next page you will see a series of 25 words. This is your unique and private seed and it is the ONLY way to recover your wallet in case of loss or malfunction. It is YOUR responsibility to write it down and store it in a safe place outside of the Cake Wallet app.", "pre_seed_description" : "On the next page you will see a series of ${words} words. This is your unique and private seed and it is the ONLY way to recover your wallet in case of loss or malfunction. It is YOUR responsibility to write it down and store it in a safe place outside of the Cake Wallet app.",
"pre_seed_button_text" : "I understand. Show me my seed", "pre_seed_button_text" : "I understand. Show me my seed",
"xmr_to_error" : "XMR.TO error", "xmr_to_error" : "XMR.TO error",
@ -428,5 +429,11 @@
"color_theme" : "Color theme", "color_theme" : "Color theme",
"light_theme" : "Light", "light_theme" : "Light",
"bright_theme" : "Bright", "bright_theme" : "Bright",
"dark_theme" : "Dark" "dark_theme" : "Dark",
"enter_your_note" : "Enter your note…",
"note_optional" : "Note (optional)",
"note_tap_to_change" : "Note (tap to change)",
"transaction_key" : "Transaction Key",
"confirmations" : "Confirmations",
"recipient_address" : "Recipient address"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "Nombre", "send_name" : "Nombre",
"send_got_it" : "Entendido", "send_got_it" : "Entendido",
"send_sending" : "Enviando...", "send_sending" : "Enviando...",
"send_success" : "Su Monero fue enviado con éxito", "send_success" : "Su ${crypto} fue enviado con éxito",
"settings_title" : "Configuraciones", "settings_title" : "Configuraciones",
@ -282,6 +282,7 @@
"transaction_details_date" : "Fecha", "transaction_details_date" : "Fecha",
"transaction_details_height" : "Altura", "transaction_details_height" : "Altura",
"transaction_details_amount" : "Cantidad", "transaction_details_amount" : "Cantidad",
"transaction_details_fee" : "Cuota",
"transaction_details_copied" : "${title} Copiado al portapapeles", "transaction_details_copied" : "${title} Copiado al portapapeles",
"transaction_details_recipient_address" : "Dirección del receptor", "transaction_details_recipient_address" : "Dirección del receptor",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "Espere hasta que su billetera esté sincronizada", "exchange_sync_alert_content" : "Espere hasta que su billetera esté sincronizada",
"pre_seed_title" : "IMPORTANTE", "pre_seed_title" : "IMPORTANTE",
"pre_seed_description" : "En la página siguiente verá una serie de 25 palabras. Esta es su semilla única y privada y es la ÚNICA forma de recuperar su billetera en caso de pérdida o mal funcionamiento. Es SU responsabilidad escribirlo y guardarlo en un lugar seguro fuera de la aplicación Cake Wallet.", "pre_seed_description" : "En la página siguiente verá una serie de ${words} palabras. Esta es su semilla única y privada y es la ÚNICA forma de recuperar su billetera en caso de pérdida o mal funcionamiento. Es SU responsabilidad escribirlo y guardarlo en un lugar seguro fuera de la aplicación Cake Wallet.",
"pre_seed_button_text" : "Entiendo. Muéstrame mi semilla", "pre_seed_button_text" : "Entiendo. Muéstrame mi semilla",
"xmr_to_error" : "Error de XMR.TO", "xmr_to_error" : "Error de XMR.TO",
@ -428,5 +429,11 @@
"color_theme" : "Tema de color", "color_theme" : "Tema de color",
"light_theme" : "Ligera", "light_theme" : "Ligera",
"bright_theme" : "Brillante", "bright_theme" : "Brillante",
"dark_theme" : "Oscura" "dark_theme" : "Oscura",
"enter_your_note" : "Ingresa tu nota…",
"note_optional" : "Nota (opcional)",
"note_tap_to_change" : "Nota (toque para cambiar)",
"transaction_key" : "Clave de transacción",
"confirmations" : "Confirmaciones",
"recipient_address" : "Dirección del receptor"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "नाम", "send_name" : "नाम",
"send_got_it" : "समझ गया", "send_got_it" : "समझ गया",
"send_sending" : "भेजना...", "send_sending" : "भेजना...",
"send_success" : "आपका Monero सफलतापूर्वक भेजा गया", "send_success" : "आपका ${crypto} सफलतापूर्वक भेजा गया",
"settings_title" : "सेटिंग्स", "settings_title" : "सेटिंग्स",
@ -282,6 +282,7 @@
"transaction_details_date" : "तारीख", "transaction_details_date" : "तारीख",
"transaction_details_height" : "ऊंचाई", "transaction_details_height" : "ऊंचाई",
"transaction_details_amount" : "रकम", "transaction_details_amount" : "रकम",
"transaction_details_fee" : "शुल्क",
"transaction_details_copied" : "${title} क्लिपबोर्ड पर नकल", "transaction_details_copied" : "${title} क्लिपबोर्ड पर नकल",
"transaction_details_recipient_address" : "प्राप्तकर्ता का पता", "transaction_details_recipient_address" : "प्राप्तकर्ता का पता",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "कृपया प्रतीक्षा करें जब तक आपका बटुआ सिंक्रनाइज़ नहीं किया जाता है", "exchange_sync_alert_content" : "कृपया प्रतीक्षा करें जब तक आपका बटुआ सिंक्रनाइज़ नहीं किया जाता है",
"pre_seed_title" : "महत्वपूर्ण", "pre_seed_title" : "महत्वपूर्ण",
"pre_seed_description" : "अगले पेज पर आपको 25 शब्दों की एक श्रृंखला दिखाई देगी। यह आपका अद्वितीय और निजी बीज है और नुकसान या खराबी के मामले में अपने बटुए को पुनर्प्राप्त करने का एकमात्र तरीका है। यह आपकी जिम्मेदारी है कि इसे नीचे लिखें और इसे Cake Wallet ऐप के बाहर सुरक्षित स्थान पर संग्रहीत करें।", "pre_seed_description" : "अगले पेज पर आपको ${words} शब्दों की एक श्रृंखला दिखाई देगी। यह आपका अद्वितीय और निजी बीज है और नुकसान या खराबी के मामले में अपने बटुए को पुनर्प्राप्त करने का एकमात्र तरीका है। यह आपकी जिम्मेदारी है कि इसे नीचे लिखें और इसे Cake Wallet ऐप के बाहर सुरक्षित स्थान पर संग्रहीत करें।",
"pre_seed_button_text" : "मै समझता हुँ। मुझे अपना बीज दिखाओ", "pre_seed_button_text" : "मै समझता हुँ। मुझे अपना बीज दिखाओ",
"xmr_to_error" : "XMR.TO त्रुटि", "xmr_to_error" : "XMR.TO त्रुटि",
@ -428,5 +429,11 @@
"color_theme" : "रंग विषय", "color_theme" : "रंग विषय",
"light_theme" : "रोशनी", "light_theme" : "रोशनी",
"bright_theme" : "उज्ज्वल", "bright_theme" : "उज्ज्वल",
"dark_theme" : "अंधेरा" "dark_theme" : "अंधेरा",
"enter_your_note" : "अपना नोट दर्ज करें ...",
"note_optional" : "नोट (वैकल्पिक)",
"note_tap_to_change" : "नोट (टैप टू चेंज)",
"transaction_key" : "लेन-देन की",
"confirmations" : "पुष्टिकरण",
"recipient_address" : "प्राप्तकर्ता का पता"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "名前", "send_name" : "名前",
"send_got_it" : "とった", "send_got_it" : "とった",
"send_sending" : "送信...", "send_sending" : "送信...",
"send_success" : "Moneroが送信されました", "send_success" : "${crypto}が送信されました",
"settings_title" : "設定", "settings_title" : "設定",
@ -282,6 +282,7 @@
"transaction_details_date" : "日付", "transaction_details_date" : "日付",
"transaction_details_height" : "高さ", "transaction_details_height" : "高さ",
"transaction_details_amount" : "量", "transaction_details_amount" : "量",
"transaction_details_fee" : "費用",
"transaction_details_copied" : "${title} クリップボードにコピーしました", "transaction_details_copied" : "${title} クリップボードにコピーしました",
"transaction_details_recipient_address" : "受取人の住所", "transaction_details_recipient_address" : "受取人の住所",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "ウォレットが同期されるまでお待ちください", "exchange_sync_alert_content" : "ウォレットが同期されるまでお待ちください",
"pre_seed_title" : "重要", "pre_seed_title" : "重要",
"pre_seed_description" : "次のページでは、一連の25語が表示されます。 これはあなたのユニークでプライベートなシードであり、紛失や誤動作が発生した場合にウォレットを回復する唯一の方法です。 それを書き留めて、Cake Wallet アプリの外の安全な場所に保管するのはあなたの責任です。", "pre_seed_description" : "次のページでは、一連の${words}語が表示されます。 これはあなたのユニークでプライベートなシードであり、紛失や誤動作が発生した場合にウォレットを回復する唯一の方法です。 それを書き留めて、Cake Wallet アプリの外の安全な場所に保管するのはあなたの責任です。",
"pre_seed_button_text" : "わかります。 種を見せて", "pre_seed_button_text" : "わかります。 種を見せて",
"xmr_to_error" : "XMR.TOエラー", "xmr_to_error" : "XMR.TOエラー",
@ -428,5 +429,11 @@
"color_theme" : "カラーテーマ", "color_theme" : "カラーテーマ",
"light_theme" : "光", "light_theme" : "光",
"bright_theme" : "明るい", "bright_theme" : "明るい",
"dark_theme" : "闇" "dark_theme" : "闇",
"enter_your_note" : "メモを入力してください…",
"note_optional" : "注(オプション)",
"note_tap_to_change" : "注(タップして変更)",
"transaction_key" : "トランザクションキー",
"confirmations" : "確認",
"recipient_address" : "受信者のアドレス"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "이름", "send_name" : "이름",
"send_got_it" : "알았다", "send_got_it" : "알았다",
"send_sending" : "배상...", "send_sending" : "배상...",
"send_success" : "Monero가 성공적으로 전송되었습니다", "send_success" : "${crypto}가 성공적으로 전송되었습니다",
"settings_title" : "설정", "settings_title" : "설정",
@ -282,6 +282,7 @@
"transaction_details_date" : "날짜", "transaction_details_date" : "날짜",
"transaction_details_height" : "신장", "transaction_details_height" : "신장",
"transaction_details_amount" : "양", "transaction_details_amount" : "양",
"transaction_details_fee" : "회비",
"transaction_details_copied" : "${title} 클립 보드에 복사", "transaction_details_copied" : "${title} 클립 보드에 복사",
"transaction_details_recipient_address" : "받는 사람 주소", "transaction_details_recipient_address" : "받는 사람 주소",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "지갑이 동기화 될 때까지 기다리십시오", "exchange_sync_alert_content" : "지갑이 동기화 될 때까지 기다리십시오",
"pre_seed_title" : "중대한", "pre_seed_title" : "중대한",
"pre_seed_description" : "다음 페이지에서 25 개의 단어를 볼 수 있습니다. 이것은 귀하의 고유하고 개인적인 시드이며 분실 또는 오작동시 지갑을 복구하는 유일한 방법입니다. 기록해두고 Cake Wallet 앱 외부의 안전한 장소에 보관하는 것은 귀하의 책임입니다.", "pre_seed_description" : "다음 페이지에서 ${words} 개의 단어를 볼 수 있습니다. 이것은 귀하의 고유하고 개인적인 시드이며 분실 또는 오작동시 지갑을 복구하는 유일한 방법입니다. 기록해두고 Cake Wallet 앱 외부의 안전한 장소에 보관하는 것은 귀하의 책임입니다.",
"pre_seed_button_text" : "이해 했어요. 내 씨앗을 보여줘", "pre_seed_button_text" : "이해 했어요. 내 씨앗을 보여줘",
"xmr_to_error" : "XMR.TO 오류", "xmr_to_error" : "XMR.TO 오류",
@ -428,5 +429,11 @@
"color_theme" : "색상 테마", "color_theme" : "색상 테마",
"light_theme" : "빛", "light_theme" : "빛",
"bright_theme" : "선명한", "bright_theme" : "선명한",
"dark_theme" : "어두운" "dark_theme" : "어두운",
"enter_your_note" : "메모를 입력하세요…",
"note_optional" : "참고 (선택 사항)",
"note_tap_to_change" : "메모 (변경하려면 탭하세요)",
"transaction_key" : "거래 키",
"confirmations" : "확인",
"recipient_address" : "받는 사람 주소"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "Naam", "send_name" : "Naam",
"send_got_it" : "Ik snap het", "send_got_it" : "Ik snap het",
"send_sending" : "Bezig met verzenden...", "send_sending" : "Bezig met verzenden...",
"send_success" : "Uw Monero is succesvol verzonden", "send_success" : "Uw ${crypto} is succesvol verzonden",
"settings_title" : "Instellingen", "settings_title" : "Instellingen",
@ -282,6 +282,7 @@
"transaction_details_date" : "Datum", "transaction_details_date" : "Datum",
"transaction_details_height" : "Hoogte", "transaction_details_height" : "Hoogte",
"transaction_details_amount" : "Bedrag", "transaction_details_amount" : "Bedrag",
"transaction_details_fee" : "Vergoeding",
"transaction_details_copied" : "${title} gekopieerd naar het klembord", "transaction_details_copied" : "${title} gekopieerd naar het klembord",
"transaction_details_recipient_address" : "Adres van de ontvanger", "transaction_details_recipient_address" : "Adres van de ontvanger",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "Wacht tot uw portemonnee is gesynchroniseerd", "exchange_sync_alert_content" : "Wacht tot uw portemonnee is gesynchroniseerd",
"pre_seed_title" : "BELANGRIJK", "pre_seed_title" : "BELANGRIJK",
"pre_seed_description" : "Op de volgende pagina ziet u een reeks van 25 woorden. Dit is uw unieke en persoonlijke zaadje en het is de ENIGE manier om uw portemonnee te herstellen in geval van verlies of storing. Het is JOUW verantwoordelijkheid om het op te schrijven en op een veilige plaats op te slaan buiten de Cake Wallet app.", "pre_seed_description" : "Op de volgende pagina ziet u een reeks van ${words} woorden. Dit is uw unieke en persoonlijke zaadje en het is de ENIGE manier om uw portemonnee te herstellen in geval van verlies of storing. Het is JOUW verantwoordelijkheid om het op te schrijven en op een veilige plaats op te slaan buiten de Cake Wallet app.",
"pre_seed_button_text" : "Ik begrijp het. Laat me mijn zaad zien", "pre_seed_button_text" : "Ik begrijp het. Laat me mijn zaad zien",
"xmr_to_error" : "XMR.TO-fout", "xmr_to_error" : "XMR.TO-fout",
@ -428,5 +429,11 @@
"color_theme" : "Kleur thema", "color_theme" : "Kleur thema",
"light_theme" : "Licht", "light_theme" : "Licht",
"bright_theme" : "Helder", "bright_theme" : "Helder",
"dark_theme" : "Donker" "dark_theme" : "Donker",
"enter_your_note" : "Voer uw notitie in ...",
"note_optional" : "Opmerking (optioneel)",
"note_tap_to_change" : "Opmerking (tik om te wijzigen)",
"transaction_key" : "Transactiesleutel",
"confirmations" : "Bevestigingen",
"recipient_address" : "Adres ontvanger"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "Imię", "send_name" : "Imię",
"send_got_it" : "Rozumiem", "send_got_it" : "Rozumiem",
"send_sending" : "Wysyłanie...", "send_sending" : "Wysyłanie...",
"send_success" : "Twoje Monero zostało pomyślnie wysłane", "send_success" : "Twoje ${crypto} zostało pomyślnie wysłane",
"settings_title" : "Ustawienia", "settings_title" : "Ustawienia",
@ -282,6 +282,7 @@
"transaction_details_date" : "Data", "transaction_details_date" : "Data",
"transaction_details_height" : "Wysokość", "transaction_details_height" : "Wysokość",
"transaction_details_amount" : "Ilość", "transaction_details_amount" : "Ilość",
"transaction_details_fee" : "Opłata",
"transaction_details_copied" : "${title} skopiowane do schowka", "transaction_details_copied" : "${title} skopiowane do schowka",
"transaction_details_recipient_address" : "Adres odbiorcy", "transaction_details_recipient_address" : "Adres odbiorcy",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "Poczekaj, aż portfel zostanie zsynchronizowany", "exchange_sync_alert_content" : "Poczekaj, aż portfel zostanie zsynchronizowany",
"pre_seed_title" : "WAŻNY", "pre_seed_title" : "WAŻNY",
"pre_seed_description" : "Na następnej stronie zobaczysz serię 25 słów. To jest Twoje unikalne i prywatne ziarno i jest to JEDYNY sposób na odzyskanie portfela w przypadku utraty lub awarii. Twoim obowiązkiem jest zapisanie go i przechowywanie w bezpiecznym miejscu poza aplikacją Cake Wallet.", "pre_seed_description" : "Na następnej stronie zobaczysz serię ${words} słów. To jest Twoje unikalne i prywatne ziarno i jest to JEDYNY sposób na odzyskanie portfela w przypadku utraty lub awarii. Twoim obowiązkiem jest zapisanie go i przechowywanie w bezpiecznym miejscu poza aplikacją Cake Wallet.",
"pre_seed_button_text" : "Rozumiem. Pokaż mi moje nasienie", "pre_seed_button_text" : "Rozumiem. Pokaż mi moje nasienie",
"xmr_to_error" : "Pomyłka XMR.TO", "xmr_to_error" : "Pomyłka XMR.TO",
@ -428,5 +429,11 @@
"color_theme" : "Motyw kolorystyczny", "color_theme" : "Motyw kolorystyczny",
"light_theme" : "Lekki", "light_theme" : "Lekki",
"bright_theme" : "Jasny", "bright_theme" : "Jasny",
"dark_theme" : "Ciemny" "dark_theme" : "Ciemny",
"enter_your_note" : "Wpisz notatkę…",
"note_optional" : "Notatka (opcjonalnie)",
"note_tap_to_change" : "Notatka (dotknij, aby zmienić)",
"transaction_key" : "Klucz transakcji",
"confirmations" : "Potwierdzenia",
"recipient_address" : "Adres odbiorcy"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "Nome", "send_name" : "Nome",
"send_got_it" : "Entendi", "send_got_it" : "Entendi",
"send_sending" : "Enviando...", "send_sending" : "Enviando...",
"send_success" : "Seu Monero foi enviado com sucesso", "send_success" : "Seu ${crypto} foi enviado com sucesso",
"settings_title" : "Configurações", "settings_title" : "Configurações",
@ -282,6 +282,7 @@
"transaction_details_date" : "Data", "transaction_details_date" : "Data",
"transaction_details_height" : "Altura", "transaction_details_height" : "Altura",
"transaction_details_amount" : "Quantia", "transaction_details_amount" : "Quantia",
"transaction_details_fee" : "Taxa",
"transaction_details_copied" : "${title} copiados para a área de transferência", "transaction_details_copied" : "${title} copiados para a área de transferência",
"transaction_details_recipient_address" : "Endereço do destinatário", "transaction_details_recipient_address" : "Endereço do destinatário",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "Por favor, espere até que sua carteira seja sincronizada", "exchange_sync_alert_content" : "Por favor, espere até que sua carteira seja sincronizada",
"pre_seed_title" : "IMPORTANTE", "pre_seed_title" : "IMPORTANTE",
"pre_seed_description" : "Na próxima página, você verá uma série de 25 palavras. Esta é a sua semente única e privada e é a ÚNICA maneira de recuperar sua carteira em caso de perda ou mau funcionamento. É SUA responsabilidade anotá-lo e armazená-lo em um local seguro fora do aplicativo Cake Wallet.", "pre_seed_description" : "Na próxima página, você verá uma série de ${words} palavras. Esta é a sua semente única e privada e é a ÚNICA maneira de recuperar sua carteira em caso de perda ou mau funcionamento. É SUA responsabilidade anotá-lo e armazená-lo em um local seguro fora do aplicativo Cake Wallet.",
"pre_seed_button_text" : "Compreendo. Me mostre minha semente", "pre_seed_button_text" : "Compreendo. Me mostre minha semente",
"xmr_to_error" : "Erro XMR.TO", "xmr_to_error" : "Erro XMR.TO",
@ -428,5 +429,11 @@
"color_theme" : "Tema de cor", "color_theme" : "Tema de cor",
"light_theme" : "Luz", "light_theme" : "Luz",
"bright_theme" : "Brilhante", "bright_theme" : "Brilhante",
"dark_theme" : "Sombria" "dark_theme" : "Sombria",
"enter_your_note" : "Insira sua nota ...",
"note_optional" : "Nota (opcional)",
"note_tap_to_change" : "Nota (toque para alterar)",
"transaction_key" : "Chave de transação",
"confirmations" : "Confirmações",
"recipient_address" : "Endereço do destinatário"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "Имя", "send_name" : "Имя",
"send_got_it" : "Понял", "send_got_it" : "Понял",
"send_sending" : "Отправка...", "send_sending" : "Отправка...",
"send_success" : "Ваш Monero был успешно отправлен", "send_success" : "Ваш ${crypto} был успешно отправлен",
"settings_title" : "Настройки", "settings_title" : "Настройки",
@ -282,6 +282,7 @@
"transaction_details_date" : "Дата", "transaction_details_date" : "Дата",
"transaction_details_height" : "Высота", "transaction_details_height" : "Высота",
"transaction_details_amount" : "Сумма", "transaction_details_amount" : "Сумма",
"transaction_details_fee" : "Комиссия",
"transaction_details_copied" : "${title} скопировано в буфер обмена", "transaction_details_copied" : "${title} скопировано в буфер обмена",
"transaction_details_recipient_address" : "Адрес получателя", "transaction_details_recipient_address" : "Адрес получателя",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "Подождите, пока ваш кошелек синхронизируется", "exchange_sync_alert_content" : "Подождите, пока ваш кошелек синхронизируется",
"pre_seed_title" : "ВАЖНО", "pre_seed_title" : "ВАЖНО",
"pre_seed_description" : "На следующей странице вы увидите серию из 25 слов. Это ваша уникальная и личная мнемоническая фраза, и это ЕДИНСТВЕННЫЙ способ восстановить свой кошелек в случае потери или неисправности. ВАМ необходимо записать ее и хранить в надежном месте вне приложения Cake Wallet.", "pre_seed_description" : "На следующей странице вы увидите серию из ${words} слов. Это ваша уникальная и личная мнемоническая фраза, и это ЕДИНСТВЕННЫЙ способ восстановить свой кошелек в случае потери или неисправности. ВАМ необходимо записать ее и хранить в надежном месте вне приложения Cake Wallet.",
"pre_seed_button_text" : "Понятно. Покажите мнемоническую фразу", "pre_seed_button_text" : "Понятно. Покажите мнемоническую фразу",
"xmr_to_error" : "Ошибка XMR.TO", "xmr_to_error" : "Ошибка XMR.TO",
@ -428,5 +429,11 @@
"color_theme" : "Цветовая тема", "color_theme" : "Цветовая тема",
"light_theme" : "Светлая", "light_theme" : "Светлая",
"bright_theme" : "Яркая", "bright_theme" : "Яркая",
"dark_theme" : "Темная" "dark_theme" : "Темная",
"enter_your_note" : "Введите примечание…",
"note_optional" : "Примечание (необязательно)",
"note_tap_to_change" : "Примечание (нажмите для изменения)",
"transaction_key" : "Ключ транзакции",
"confirmations" : "Подтверждения",
"recipient_address" : "Адрес получателя"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "Ім'я", "send_name" : "Ім'я",
"send_got_it" : "Зрозумів", "send_got_it" : "Зрозумів",
"send_sending" : "Відправлення...", "send_sending" : "Відправлення...",
"send_success" : "Ваш Monero успішно надісланий", "send_success" : "Ваш ${crypto} успішно надісланий",
"settings_title" : "Налаштування", "settings_title" : "Налаштування",
@ -282,6 +282,7 @@
"transaction_details_date" : "Дата", "transaction_details_date" : "Дата",
"transaction_details_height" : "Висота", "transaction_details_height" : "Висота",
"transaction_details_amount" : "Сума", "transaction_details_amount" : "Сума",
"transaction_details_fee" : "Комісія",
"transaction_details_copied" : "${title} скопійовано в буфер обміну", "transaction_details_copied" : "${title} скопійовано в буфер обміну",
"transaction_details_recipient_address" : "Адреса отримувача", "transaction_details_recipient_address" : "Адреса отримувача",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "Зачекайте, поки ваш гаманець не синхронізується", "exchange_sync_alert_content" : "Зачекайте, поки ваш гаманець не синхронізується",
"pre_seed_title" : "ВАЖЛИВО", "pre_seed_title" : "ВАЖЛИВО",
"pre_seed_description" : "На наступній сторінці ви побачите серію з 25 слів. Це ваша унікальна та приватна мнемонічна фраза, і це ЄДИНИЙ спосіб відновити ваш гаманець на випадок втрати або несправності. ВАМ необхідно записати її та зберігати в безпечному місці поза програмою Cake Wallet.", "pre_seed_description" : "На наступній сторінці ви побачите серію з ${words} слів. Це ваша унікальна та приватна мнемонічна фраза, і це ЄДИНИЙ спосіб відновити ваш гаманець на випадок втрати або несправності. ВАМ необхідно записати її та зберігати в безпечному місці поза програмою Cake Wallet.",
"pre_seed_button_text" : "Зрозуміло. Покажіть мнемонічну фразу", "pre_seed_button_text" : "Зрозуміло. Покажіть мнемонічну фразу",
"xmr_to_error" : "Помилка XMR.TO", "xmr_to_error" : "Помилка XMR.TO",
@ -428,5 +429,11 @@
"color_theme" : "Кольорова тема", "color_theme" : "Кольорова тема",
"light_theme" : "Світла", "light_theme" : "Світла",
"bright_theme" : "Яскрава", "bright_theme" : "Яскрава",
"dark_theme" : "Темна" "dark_theme" : "Темна",
"enter_your_note" : "Введіть примітку…",
"note_optional" : "Примітка (необов’язково)",
"note_tap_to_change" : "Примітка (натисніть для зміни)",
"transaction_key" : "Ключ транзакції",
"confirmations" : "Підтвердження",
"recipient_address" : "Адреса одержувача"
} }

View file

@ -212,7 +212,7 @@
"send_name" : "名稱", "send_name" : "名稱",
"send_got_it" : "得到它了", "send_got_it" : "得到它了",
"send_sending" : "正在發送...", "send_sending" : "正在發送...",
"send_success" : "你Monero已成功發送", "send_success" : "你${crypto}已成功發送",
"settings_title" : "设定值", "settings_title" : "设定值",
@ -282,6 +282,7 @@
"transaction_details_date" : "日期", "transaction_details_date" : "日期",
"transaction_details_height" : "高度", "transaction_details_height" : "高度",
"transaction_details_amount" : "量", "transaction_details_amount" : "量",
"transaction_details_fee" : "費用",
"transaction_details_copied" : "${title} 复制到剪贴板", "transaction_details_copied" : "${title} 复制到剪贴板",
"transaction_details_recipient_address" : "收件人地址", "transaction_details_recipient_address" : "收件人地址",
@ -415,7 +416,7 @@
"exchange_sync_alert_content" : "請等待,直到您的錢包同步", "exchange_sync_alert_content" : "請等待,直到您的錢包同步",
"pre_seed_title" : "重要", "pre_seed_title" : "重要",
"pre_seed_description" : "在下一頁上,您將看到一系列25個單詞。 這是您獨特的私人種子,是丟失或出現故障時恢復錢包的唯一方法。 您有責任將其寫下並存儲在Cake Wallet應用程序外部的安全地方。", "pre_seed_description" : "在下一頁上,您將看到一系列${words}個單詞。 這是您獨特的私人種子,是丟失或出現故障時恢復錢包的唯一方法。 您有責任將其寫下並存儲在Cake Wallet應用程序外部的安全地方。",
"pre_seed_button_text" : "我明白。 給我看我的種子", "pre_seed_button_text" : "我明白。 給我看我的種子",
"xmr_to_error" : "XMR.TO錯誤", "xmr_to_error" : "XMR.TO錯誤",
@ -428,5 +429,11 @@
"color_theme" : "顏色主題", "color_theme" : "顏色主題",
"light_theme" : "光", "light_theme" : "光",
"bright_theme" : "亮", "bright_theme" : "亮",
"dark_theme" : "黑暗" "dark_theme" : "黑暗",
"enter_your_note" : "輸入您的筆記...",
"note_optional" : "注意(可選)",
"note_tap_to_change" : "注意(輕按即可更改)",
"transaction_key" : "交易密碼",
"confirmations" : "確認書",
"recipient_address" : "收件人地址"
} }