CW-325-Coin-Control-enhancements (#846)

* fix checkbox

* save the output state

* add note as a header

* Allow copy the Amount and Address

* add frozen balance to dashboard

* add block explorer

* fix url launcher

* code formatting

* minor fixes

* Revert "minor fixes"

This reverts commit d230b6a07b.

* fix missing implementations error

* [skip ci] update localization

* fix unspent with same txid

* add amount check

* add vout check

* remove formattedTotalAvailableBalance

* remove unrelated mac os files
This commit is contained in:
Serhii 2023-04-20 16:46:41 +03:00 committed by GitHub
parent f26815efb8
commit 315c4c911c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 525 additions and 339 deletions

View file

@ -4,7 +4,7 @@ import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_core/balance.dart';
class ElectrumBalance extends Balance {
const ElectrumBalance({required this.confirmed, required this.unconfirmed})
const ElectrumBalance({required this.confirmed, required this.unconfirmed, required this.frozen})
: super(confirmed, unconfirmed);
static ElectrumBalance? fromJSON(String? jsonSource) {
@ -16,20 +16,25 @@ class ElectrumBalance extends Balance {
return ElectrumBalance(
confirmed: decoded['confirmed'] as int? ?? 0,
unconfirmed: decoded['unconfirmed'] as int? ?? 0);
unconfirmed: decoded['unconfirmed'] as int? ?? 0,
frozen: decoded['frozen'] as int? ?? 0);
}
final int confirmed;
final int unconfirmed;
final int frozen;
@override
String get formattedAvailableBalance =>
bitcoinAmountToString(amount: confirmed);
String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed - frozen);
@override
String get formattedAdditionalBalance =>
bitcoinAmountToString(amount: unconfirmed);
String get formattedAdditionalBalance => bitcoinAmountToString(amount: unconfirmed);
String get formattedFrozenBalance {
final frozenFormatted = bitcoinAmountToString(amount: frozen);
return frozenFormatted == '0.0' ? '' : frozenFormatted;
}
String toJSON() =>
json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed});
json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed, 'frozen': frozen});
}

View file

@ -63,7 +63,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
_scripthashesUpdateSubject = {},
balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(
currency != null
? {currency: initialBalance ?? const ElectrumBalance(confirmed: 0, unconfirmed: 0)}
? {currency: initialBalance ?? const ElectrumBalance(confirmed: 0, unconfirmed: 0,
frozen: 0)}
: {}),
this.unspentCoinsInfo = unspentCoinsInfo,
super(walletInfo) {
@ -133,8 +134,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
await walletAddresses.discoverAddresses();
await updateTransactions();
_subscribeForUpdates();
await _updateBalance();
await updateUnspent();
await updateBalance();
_feeRates = await electrumClient.feeRates();
Timer.periodic(const Duration(minutes: 1),
@ -343,7 +344,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
electrumClient: electrumClient, amount: amount, fee: fee)
..addListener((transaction) async {
transactionHistory.addOne(transaction);
await _updateBalance();
await updateBalance();
});
}
@ -497,7 +498,10 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
hash: coin.hash,
isFrozen: coin.isFrozen,
isSending: coin.isSending,
noteRaw: coin.note
noteRaw: coin.note,
address: coin.address.address,
value: coin.value,
vout: coin.vout,
);
await unspentCoinsInfo.add(newInfo);
@ -634,8 +638,8 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
_scripthashesUpdateSubject[sh] = electrumClient.scripthashUpdate(sh);
_scripthashesUpdateSubject[sh]?.listen((event) async {
try {
await _updateBalance();
await updateUnspent();
await updateBalance();
await updateTransactions();
} catch (e) {
print(e.toString());
@ -653,7 +657,17 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
final sh = scriptHash(addressRecord.address, networkType: networkType);
final balanceFuture = electrumClient.getBalance(sh);
balanceFutures.add(balanceFuture);
}
}
var totalFrozen = 0;
unspentCoinsInfo.values.forEach((info) {
unspentCoins.forEach((element) {
if (element.hash == info.hash && info.isFrozen && element.address.address == info.address
&& element.value == info.value) {
totalFrozen += element.value;
}
});
});
final balances = await Future.wait(balanceFutures);
var totalConfirmed = 0;
@ -672,10 +686,11 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
}
}
return ElectrumBalance(confirmed: totalConfirmed, unconfirmed: totalUnconfirmed);
return ElectrumBalance(confirmed: totalConfirmed, unconfirmed: totalUnconfirmed,
frozen: totalFrozen);
}
Future<void> _updateBalance() async {
Future<void> updateBalance() async {
balance[currency] = await _fetchBalances();
await save();
}

View file

@ -37,7 +37,7 @@ class ElectrumWallletSnapshot {
.map((addr) => BitcoinAddressRecord.fromJSON(addr))
.toList();
final balance = ElectrumBalance.fromJSON(data['balance'] as String) ??
ElectrumBalance(confirmed: 0, unconfirmed: 0);
ElectrumBalance(confirmed: 0, unconfirmed: 0, frozen: 0);
var regularAddressIndex = 0;
var changeAddressIndex = 0;

View file

@ -9,7 +9,10 @@ class UnspentCoinsInfo extends HiveObject {
required this.hash,
required this.isFrozen,
required this.isSending,
required this.noteRaw});
required this.noteRaw,
required this.address,
required this.vout,
required this.value});
static const typeId = 9;
static const boxName = 'Unspent';
@ -30,6 +33,15 @@ class UnspentCoinsInfo extends HiveObject {
@HiveField(4)
String? noteRaw;
@HiveField(5, defaultValue: '')
String address;
@HiveField(6, defaultValue: 0)
int value;
@HiveField(7, defaultValue: 0)
int vout;
String get note => noteRaw ?? '';
set note(String value) => noteRaw = value;

View file

@ -71,4 +71,6 @@ abstract class WalletBase<
void close();
Future<void> changePassword(String password);
Future<void>? updateBalance();
}

View file

@ -104,6 +104,9 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
(_) async => await save());
}
@override
Future<void>? updateBalance() => null;
@override
void close() {
_listener?.stop();

View file

@ -118,6 +118,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
Duration(seconds: _autoSaveInterval),
(_) async => await save());
}
@override
Future<void>? updateBalance() => null;
@override
void close() {

View file

@ -8,187 +8,207 @@ import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/src/widgets/introducing_card.dart';
import 'package:cake_wallet/generated/i18n.dart';
class BalancePage extends StatelessWidget{
class BalancePage extends StatelessWidget {
BalancePage({required this.dashboardViewModel, required this.settingsStore});
final DashboardViewModel dashboardViewModel;
final SettingsStore settingsStore;
Color get backgroundLightColor =>
settingsStore.currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@override
Widget build(BuildContext context) {
return GestureDetector(
onLongPress: () => dashboardViewModel.balanceViewModel.isReversing = !dashboardViewModel.balanceViewModel.isReversing,
onLongPressUp: () => dashboardViewModel.balanceViewModel.isReversing = !dashboardViewModel.balanceViewModel.isReversing,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: ResponsiveLayoutUtil.instance.isMobile(context) ? 56 : 16),
onLongPress: () => dashboardViewModel.balanceViewModel.isReversing =
!dashboardViewModel.balanceViewModel.isReversing,
onLongPressUp: () => dashboardViewModel.balanceViewModel.isReversing =
!dashboardViewModel.balanceViewModel.isReversing,
child: SingleChildScrollView(
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
SizedBox(height: 56),
Container(
margin: const EdgeInsets.only(left: 24, bottom: 16),
child: Observer(builder: (_) {
return Text(
dashboardViewModel.balanceViewModel.asset,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.backgroundColor!,
height: 1),
maxLines: 1,
textAlign: TextAlign.center);
})),
margin: const EdgeInsets.only(left: 24, bottom: 16),
child: Observer(builder: (_) {
return Text(dashboardViewModel.balanceViewModel.asset,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
height: 1),
maxLines: 1,
textAlign: TextAlign.center);
})),
Observer(builder: (_) {
if (dashboardViewModel.balanceViewModel.isShowCard){
if (dashboardViewModel.balanceViewModel.isShowCard) {
return IntroducingCard(
title: S.of(context).introducing_cake_pay,
title: S.of(context).introducing_cake_pay,
subTitle: S.of(context).cake_pay_learn_more,
borderColor: settingsStore.currentTheme.type == ThemeType.bright
? Color.fromRGBO(255, 255, 255, 0.2)
: Colors.transparent,
closeCard: dashboardViewModel.balanceViewModel.disableIntroCakePayCard
);
closeCard: dashboardViewModel.balanceViewModel.disableIntroCakePayCard);
}
return Container ();
return Container();
}),
Observer(builder: (_) {
return ListView.separated(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (_, __) => Container(padding: EdgeInsets.only(bottom: 8)),
itemCount: dashboardViewModel.balanceViewModel.formattedBalances.length,
itemBuilder: (__, index) {
final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index);
return buildBalanceRow(context,
availableBalanceLabel: '${dashboardViewModel.balanceViewModel.availableBalanceLabel}',
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (_, __) => Container(padding: EdgeInsets.only(bottom: 8)),
itemCount: dashboardViewModel.balanceViewModel.formattedBalances.length,
itemBuilder: (__, index) {
final balance =
dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index);
return buildBalanceRow(context,
availableBalanceLabel:
'${dashboardViewModel.balanceViewModel.availableBalanceLabel}',
availableBalance: balance.availableBalance,
availableFiatBalance: balance.fiatAvailableBalance,
additionalBalanceLabel: '${dashboardViewModel.balanceViewModel.additionalBalanceLabel}',
additionalBalanceLabel:
'${dashboardViewModel.balanceViewModel.additionalBalanceLabel}',
additionalBalance: balance.additionalBalance,
additionalFiatBalance: balance.fiatAdditionalBalance,
frozenBalance: balance.frozenBalance,
frozenFiatBalance: balance.fiatFrozenBalance,
currency: balance.formattedAssetTitle);
});
})
])));
});
})
])));
}
Widget buildBalanceRow(BuildContext context,
{required String availableBalanceLabel,
{required String availableBalanceLabel,
required String availableBalance,
required String availableFiatBalance,
required String additionalBalanceLabel,
required String additionalBalance,
required String additionalFiatBalance,
required String frozenBalance,
required String frozenFiatBalance,
required String currency}) {
return Container(
return Container(
margin: const EdgeInsets.only(left: 16, right: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
border: Border.all(color: settingsStore.currentTheme.type == ThemeType.bright ? Color.fromRGBO(255, 255, 255, 0.2): Colors.transparent, width: 1, ),
color:Theme.of(context).textTheme!.headline6!.backgroundColor!
),
borderRadius: BorderRadius.circular(30.0),
border: Border.all(
color: settingsStore.currentTheme.type == ThemeType.bright
? Color.fromRGBO(255, 255, 255, 0.2)
: Colors.transparent,
width: 1,
),
color: Theme.of(context).textTheme!.headline6!.backgroundColor!),
child: Container(
margin: const EdgeInsets.only(top: 16, left: 24, right: 24, bottom: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 4,),
Text('${availableBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
margin: const EdgeInsets.only(top: 16, left: 24, right: 24, bottom: 24),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
SizedBox(
height: 4,
),
Text('${availableBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!,
color: Theme.of(context).accentTextTheme!.headline3!.backgroundColor!,
height: 1)),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AutoSizeText(
availableBalance,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.backgroundColor!,
height: 1),
maxLines: 1,
textAlign: TextAlign.center),
Text(currency,
style: TextStyle(
fontSize: 28,
fontFamily: 'Lato',
fontWeight: FontWeight.w800,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.backgroundColor!,
height: 1)),
]),
SizedBox(height: 4,),
Text('${availableFiatBalance}',
SizedBox(height: 5),
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
AutoSizeText(availableBalance,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
height: 1),
maxLines: 1,
textAlign: TextAlign.center),
Text(currency,
style: TextStyle(
fontSize: 28,
fontFamily: 'Lato',
fontWeight: FontWeight.w800,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
height: 1)),
]),
SizedBox(
height: 4,
),
Text('${availableFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.backgroundColor!,
height: 1)),
SizedBox(height: 26),
Text('${additionalBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.accentTextTheme!
.headline3!
.backgroundColor!,
height: 1)),
SizedBox(height: 8),
AutoSizeText(
additionalBalance,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
height: 1)),
SizedBox(height: 26),
if (frozenBalance.isNotEmpty)
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(S.current.frozen_balance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.backgroundColor!,
color: Theme.of(context).accentTextTheme!.headline3!.backgroundColor!,
height: 1)),
SizedBox(height: 8),
AutoSizeText(frozenBalance,
style: TextStyle(
fontSize: 20,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
height: 1),
maxLines: 1,
textAlign: TextAlign.center),
SizedBox(height: 4,),
Text('${additionalFiatBalance}',
SizedBox(height: 4),
Text(
frozenFiatBalance,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
height: 1),
),
SizedBox(height: 24)
]),
Text('${additionalBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).accentTextTheme!.headline3!.backgroundColor!,
height: 1)),
SizedBox(height: 8),
AutoSizeText(additionalBalance,
style: TextStyle(
fontSize: 20,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
height: 1),
maxLines: 1,
textAlign: TextAlign.center),
SizedBox(
height: 4,
),
Text(
'${additionalFiatBalance}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w400,
color: Theme.of(context)
.accentTextTheme!
.headline2!
.backgroundColor!,
color: Theme.of(context).accentTextTheme!.headline2!.backgroundColor!,
height: 1),
)
])),
)
])),
);
}
}

View file

@ -1,13 +1,16 @@
import 'package:cake_wallet/src/screens/transaction_details/blockexplorer_list_item.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/screens/unspent_coins/widgets/unspent_coins_switch_row.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_details_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/list_row.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
@ -30,9 +33,13 @@ class UnspentCoinsDetailsPage extends BasePage {
final item = unspentCoinsDetailsViewModel.items[index];
if (item is StandartListItem) {
return ListRow(
title: '${item.title}:',
value: item.value);
return GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: item.value));
showBar<void>(context, S.of(context).transaction_details_copied(item.title));
},
child: ListRow(title: '${item.title}:', value: item.value),
);
}
if (item is TextFieldListItem) {
@ -44,14 +51,21 @@ class UnspentCoinsDetailsPage extends BasePage {
}
if (item is UnspentCoinsSwitchItem) {
return Observer(builder: (_) => UnspentCoinsSwitchRow(
title: item.title,
switchValue: item.switchValue(),
onSwitchValueChange: item.onSwitchValueChange
));
return Observer(
builder: (_) => UnspentCoinsSwitchRow(
title: item.title,
switchValue: item.switchValue(),
onSwitchValueChange: item.onSwitchValueChange));
}
if (item is BlockExplorerListItem) {
return GestureDetector(
onTap: item.onTap,
child: ListRow(title: '${item.title}:', value: item.value),
);
}
return Container();
});
}
}
}

View file

@ -1,5 +1,6 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
@ -28,99 +29,77 @@ class UnspentCoinsListItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final itemColor = isSending? selectedItemColor : unselectedItemColor;
final _note = (note?.isNotEmpty ?? false) ? note : address;
final itemColor = isSending ? selectedItemColor : unselectedItemColor;
return Container(
height: 62,
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: itemColor),
height: 70,
padding: EdgeInsets.symmetric(vertical: 6, horizontal: 12),
decoration:
BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(12)), color: itemColor),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(right: 12),
child: GestureDetector(
onTap: () => onCheckBoxTap?.call(),
child: Container(
height: 24.0,
width: 24.0,
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context)
.primaryTextTheme!
.caption!
.color!,
width: 1.0),
borderRadius: BorderRadius.all(
Radius.circular(8.0)),
color: itemColor),
child: isSending
? Icon(
Icons.check,
color: Colors.blue,
size: 20.0,
)
: Offstage(),
)
)
),
child: StandardCheckbox(
value: isSending, onChanged: (value) => onCheckBoxTap?.call())),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: AutoSizeText(
amount,
style: TextStyle(
color: amountColor,
fontSize: 16,
fontWeight: FontWeight.w600
),
maxLines: 1,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (note.isNotEmpty)
AutoSizeText(
note,
style: TextStyle(
color: amountColor, fontSize: 15, fontWeight: FontWeight.w600),
maxLines: 1,
),
if (isFrozen) Container(
AutoSizeText(
amount,
style:
TextStyle(color: amountColor, fontSize: 15, fontWeight: FontWeight.w600),
maxLines: 1,
)
]),
if (isFrozen)
Container(
height: 17,
padding: EdgeInsets.only(left: 6, right: 6),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(8.5)),
color: Colors.white),
borderRadius: BorderRadius.all(Radius.circular(8.5)),
color: Colors.white),
alignment: Alignment.center,
child: Text(
S.of(context).frozen,
style: TextStyle(
color: amountColor,
fontSize: 7,
fontWeight: FontWeight.w600
),
)
)
],
),
Text(
_note,
style: TextStyle(
color: addressColor,
fontSize: 12,
S.of(context).frozen,
style:
TextStyle(color: amountColor, fontSize: 7, fontWeight: FontWeight.w600),
))
],
),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AutoSizeText(
address,
style: TextStyle(
color: addressColor,
fontSize: 12,
),
maxLines: 1,
),
maxLines: 1,
overflow: TextOverflow.ellipsis
)
]
)
)
],
),
),
])),
],
)
);
));
}
}
}

View file

@ -1,5 +1,9 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
import 'package:cw_bitcoin/bitcoin_amount_format.dart';
import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/balance.dart';
import 'package:cw_core/crypto_currency.dart';
@ -11,6 +15,7 @@ import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
part 'balance_view_model.g.dart';
@ -19,14 +24,18 @@ class BalanceRecord {
const BalanceRecord({
required this.availableBalance,
required this.additionalBalance,
required this.frozenBalance,
required this.fiatAvailableBalance,
required this.fiatAdditionalBalance,
required this.fiatFrozenBalance,
required this.asset,
required this.formattedAssetTitle});
final String fiatAdditionalBalance;
final String fiatAvailableBalance;
final String fiatFrozenBalance;
final String additionalBalance;
final String availableBalance;
final String frozenBalance;
final CryptoCurrency asset;
final String formattedAssetTitle;
}
@ -135,6 +144,32 @@ abstract class BalanceViewModelBase with Store {
return walletBalance.formattedAvailableBalance;
}
@computed
String get frozenBalance {
final walletBalance = _walletBalance;
if (displayMode == BalanceDisplayMode.hiddenBalance) {
return '---';
}
return getFormattedFrozenBalance(walletBalance);
}
@computed
String get frozenFiatBalance {
final walletBalance = _walletBalance;
final fiatCurrency = settingsStore.fiatCurrency;
if (displayMode == BalanceDisplayMode.hiddenBalance) {
return '---';
}
return _getFiatBalance(
price: price,
cryptoAmount: getFormattedFrozenBalance(walletBalance)) + ' ' + fiatCurrency.toString();
}
@computed
String get additionalBalance {
final walletBalance = _walletBalance;
@ -158,7 +193,7 @@ abstract class BalanceViewModelBase with Store {
return _getFiatBalance(
price: price,
cryptoAmount: walletBalance.formattedAvailableBalance) + ' ' + fiatCurrency.toString();
}
@computed
@ -173,7 +208,7 @@ abstract class BalanceViewModelBase with Store {
return _getFiatBalance(
price: price,
cryptoAmount: walletBalance.formattedAdditionalBalance) + ' ' + fiatCurrency.toString();
}
@computed
@ -183,8 +218,10 @@ abstract class BalanceViewModelBase with Store {
return MapEntry(key, BalanceRecord(
availableBalance: '---',
additionalBalance: '---',
frozenBalance: '---',
fiatAdditionalBalance: isFiatDisabled ? '' : '---',
fiatAvailableBalance: isFiatDisabled ? '' : '---',
fiatFrozenBalance: isFiatDisabled ? '' : '---',
asset: key,
formattedAssetTitle: _formatterAsset(key)));
}
@ -207,13 +244,25 @@ abstract class BalanceViewModelBase with Store {
price: price,
cryptoAmount: value.formattedAvailableBalance));
return MapEntry(key, BalanceRecord(
availableBalance: value.formattedAvailableBalance,
additionalBalance: value.formattedAdditionalBalance,
fiatAdditionalBalance: additionalFiatBalance,
fiatAvailableBalance: availableFiatBalance,
asset: key,
formattedAssetTitle: _formatterAsset(key)));
final frozenFiatBalance = isFiatDisabled ? '' : (fiatCurrency.toString()
+ ' '
+ _getFiatBalance(
price: price,
cryptoAmount: getFormattedFrozenBalance(value)));
return MapEntry(
key,
BalanceRecord(
availableBalance: value.formattedAvailableBalance,
additionalBalance: value.formattedAdditionalBalance,
frozenBalance: getFormattedFrozenBalance(value),
fiatAdditionalBalance: additionalFiatBalance,
fiatAvailableBalance: availableFiatBalance,
fiatFrozenBalance: frozenFiatBalance,
asset: key,
formattedAssetTitle: _formatterAsset(key)));
});
}
@ -290,7 +339,7 @@ abstract class BalanceViewModelBase with Store {
}
String _getFiatBalance({required double price, String? cryptoAmount}) {
if (cryptoAmount == null) {
if (cryptoAmount == null || cryptoAmount.isEmpty) {
return '0.00';
}
@ -306,10 +355,15 @@ abstract class BalanceViewModelBase with Store {
return assetStringified.replaceFirst('X', 'x');
}
return asset.toString();
return asset.toString();
default:
return asset.toString();
}
}
String getFormattedFrozenBalance(Balance walletBalance) =>
walletBalance is ElectrumBalance ? walletBalance.formattedFrozenBalance : '';
}

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/src/screens/transaction_details/blockexplorer_list_item.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';
@ -5,7 +6,9 @@ import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_list_view_model.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_switch_item.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:mobx/mobx.dart';
import 'package:url_launcher/url_launcher.dart';
part 'unspent_coins_details_view_model.g.dart';
@ -13,21 +16,14 @@ class UnspentCoinsDetailsViewModel = UnspentCoinsDetailsViewModelBase
with _$UnspentCoinsDetailsViewModel;
abstract class UnspentCoinsDetailsViewModelBase with Store {
UnspentCoinsDetailsViewModelBase({
required this.unspentCoinsItem,
required this.unspentCoinsListViewModel})
UnspentCoinsDetailsViewModelBase(
{required this.unspentCoinsItem, required this.unspentCoinsListViewModel})
: items = <TransactionDetailsListItem>[],
isFrozen = unspentCoinsItem.isFrozen,
note = unspentCoinsItem.note {
items = [
StandartListItem(
title: S.current.transaction_details_amount,
value: unspentCoinsItem.amount
),
StandartListItem(
title: S.current.widgets_address,
value: unspentCoinsItem.address
),
StandartListItem(title: S.current.transaction_details_amount, value: unspentCoinsItem.amount),
StandartListItem(title: S.current.widgets_address, value: unspentCoinsItem.address),
TextFieldListItem(
title: S.current.note_tap_to_change,
value: note,
@ -36,21 +32,53 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem);
}),
UnspentCoinsSwitchItem(
title: S.current.freeze,
value: '',
switchValue: () => isFrozen,
onSwitchValueChange: (value) async {
isFrozen = value;
unspentCoinsItem.isFrozen = value;
if (value) {
unspentCoinsItem.isSending = !value;
}
await unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem);
}
)
title: S.current.freeze,
value: '',
switchValue: () => isFrozen,
onSwitchValueChange: (value) async {
isFrozen = value;
unspentCoinsItem.isFrozen = value;
if (value) {
unspentCoinsItem.isSending = !value;
}
await unspentCoinsListViewModel.saveUnspentCoinInfo(unspentCoinsItem);
}),
BlockExplorerListItem(
title: S.current.view_in_block_explorer,
value: _explorerDescription(unspentCoinsListViewModel.wallet.type),
onTap: () {
try {
final url = Uri.parse(
_explorerUrl(unspentCoinsListViewModel.wallet.type, unspentCoinsItem.hash));
return launchUrl(url);
} catch (e) {}
})
];
}
String _explorerUrl(WalletType type, String txId) {
switch (type) {
case WalletType.bitcoin:
return 'https://ordinals.com/tx/${txId}';
case WalletType.litecoin:
return 'https://litecoin.earlyordies.com/tx/${txId}';
default:
return '';
}
}
String _explorerDescription(WalletType type) {
switch (type) {
case WalletType.bitcoin:
return S.current.view_transaction_on + 'Ordinals.com';
case WalletType.litecoin:
return S.current.view_transaction_on + 'Earlyordies.com';
default:
return '';
}
}
@observable
bool isFrozen;
@ -60,4 +88,4 @@ abstract class UnspentCoinsDetailsViewModelBase with Store {
final UnspentCoinsItem unspentCoinsItem;
final UnspentCoinsListViewModel unspentCoinsListViewModel;
List<TransactionDetailsListItem> items;
}
}

View file

@ -11,7 +11,9 @@ abstract class UnspentCoinsItemBase with Store {
required this.hash,
required this.isFrozen,
required this.note,
required this.isSending});
required this.isSending,
required this.amountRaw,
required this.vout});
@observable
String address;
@ -30,4 +32,10 @@ abstract class UnspentCoinsItemBase with Store {
@observable
bool isSending;
@observable
int amountRaw;
@observable
int vout;
}

View file

@ -1,60 +1,81 @@
//import 'package:cw_bitcoin/bitcoin_amount_format.dart';
//import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:collection/collection.dart';
part 'unspent_coins_list_view_model.g.dart';
class UnspentCoinsListViewModel = UnspentCoinsListViewModelBase with _$UnspentCoinsListViewModel;
abstract class UnspentCoinsListViewModelBase with Store {
UnspentCoinsListViewModelBase({
required this.wallet,
required Box<UnspentCoinsInfo> unspentCoinsInfo})
: _unspentCoinsInfo = unspentCoinsInfo {
UnspentCoinsListViewModelBase(
{required this.wallet, required Box<UnspentCoinsInfo> unspentCoinsInfo})
: _unspentCoinsInfo = unspentCoinsInfo {
bitcoin!.updateUnspents(wallet);
}
WalletBase wallet;
Box<UnspentCoinsInfo> _unspentCoinsInfo;
final Box<UnspentCoinsInfo> _unspentCoinsInfo;
@computed
ObservableList<UnspentCoinsItem> get items => ObservableList.of(bitcoin!.getUnspents(wallet).map((elem) {
final amount = bitcoin!.formatterBitcoinAmountToString(amount: elem.value) +
' ${wallet.currency.title}';
final info = _unspentCoinsInfo.values
.firstWhere((element) => element.walletId == wallet.id && element.hash == elem.hash);
ObservableList<UnspentCoinsItem> get items =>
ObservableList.of(bitcoin!.getUnspents(wallet).map((elem) {
final amount = bitcoin!.formatterBitcoinAmountToString(amount: elem.value) +
' ${wallet.currency.title}';
return UnspentCoinsItem(
address: elem.address,
amount: amount,
hash: elem.hash,
isFrozen: elem.isFrozen,
note: info.note,
isSending: elem.isSending
);
}));
final info = getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout);
return UnspentCoinsItem(
address: elem.address,
amount: amount,
hash: elem.hash,
isFrozen: info?.isFrozen ?? false,
note: info?.note ?? '',
isSending: info?.isSending ?? true,
amountRaw: elem.value,
vout: elem.vout);
}));
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
try {
final info = _unspentCoinsInfo.values
.firstWhere((element) => element.walletId.contains(wallet.id) &&
element.hash.contains(item.hash));
final info = getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout);
if (info == null) {
final newInfo = UnspentCoinsInfo(
walletId: wallet.id,
hash: item.hash,
address: item.address,
value: item.amountRaw,
vout: item.vout,
isFrozen: item.isFrozen,
isSending: item.isSending,
noteRaw: item.note);
await _unspentCoinsInfo.add(newInfo);
bitcoin!.updateUnspents(wallet);
wallet.updateBalance();
return;
}
info.isFrozen = item.isFrozen;
info.isSending = item.isSending;
info.note = item.note;
await info.save();
bitcoin!.updateUnspents(wallet);
wallet.updateBalance();
} catch (e) {
print(e.toString());
}
}
}
UnspentCoinsInfo? getUnspentCoinInfo(String hash, String address, int value, int vout) {
return _unspentCoinsInfo.values.firstWhereOrNull((element) =>
element.walletId == wallet.id &&
element.hash == hash &&
element.address == address &&
element.value == value &&
element.vout == vout);
}
}

View file

@ -479,8 +479,8 @@
"xrp_extra_info":"من فضلك لا تنس تحديد علامة الوجهة أثناء إرسال معاملة XRP للتبادل",
"exchange_incorrect_current_wallet_for_xmr":"إذا كنت ترغب في استبدال XMR من رصيد Cake Wallet Monero ، فيرجى التبديل إلى محفظة Monero أولاً.",
"confirmed":"مؤكد",
"unconfirmed":"غير مؤكد",
"confirmed":"رصيد مؤكد",
"unconfirmed":"رصيد غير مؤكد",
"displayable":"قابل للعرض",
"submit_request":"تقديم طلب",
@ -685,6 +685,7 @@
"error_dialog_content": "عفوًا ، لقد حصلنا على بعض الخطأ.\n\nيرجى إرسال تقرير التعطل إلى فريق الدعم لدينا لتحسين التطبيق.",
"decimal_places_error": "عدد كبير جدًا من المنازل العشرية",
"edit_node": "تحرير العقدة",
"frozen_balance": "الرصيد المجمد",
"invoice_details": "تفاصيل الفاتورة",
"donation_link_details": "تفاصيل رابط التبرع",
"anonpay_description": "توليد ${type}. يمكن للمستلم ${method} بأي عملة مشفرة مدعومة ، وستتلقى أموالاً في هذه",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Не забравяйте да дадете Destination Tag-а, когато изпращате XRP транзакцията за обмена",
"exchange_incorrect_current_wallet_for_xmr" : "Ако искате да обмените XMR от своя Cake Wallet Monero баланс, първо изберете своя Monero портфейл.",
"confirmed" : "Потвърдено",
"unconfirmed" : "Непотвърдено",
"confirmed" : "Потвърден баланс",
"unconfirmed" : "Непотвърден баланс",
"displayable" : "Възможност за показване",
"submit_request" : "изпращане на заявка",
@ -687,6 +687,7 @@
"error_dialog_content": "Получихме грешка.\n\nМоля, изпратете доклада до нашия отдел поддръжка, за да подобрим приложението.",
"decimal_places_error": "Твърде много знаци след десетичната запетая",
"edit_node": "Редактиране на възел",
"frozen_balance": "Замразен баланс",
"invoice_details": "IДанни за фактура",
"donation_link_details": "Подробности за връзката за дарение",
"anonpay_description": "Генерирайте ${type}. Получателят може да ${method} с всяка поддържана криптовалута и вие ще получите средства в този портфейл.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Prosím nezapomeňte zadat Destination Tag, když posíláte XRP transakce ke směně",
"exchange_incorrect_current_wallet_for_xmr" : "Pokud chcete směnit XMR z Monero částky v Cake Wallet, prosím přepněte se nejprve do své Monero peněženky.",
"confirmed" : "Potvrzeno",
"unconfirmed" : "Nepotvrzeno",
"confirmed" : "Potvrzený zůstatek",
"unconfirmed" : "Nepotvrzený zůstatek",
"displayable" : "Zobrazitelné",
"submit_request" : "odeslat požadavek",
@ -687,6 +687,7 @@
"error_dialog_content": "Nastala chyba.\n\nProsím odešlete zprávu o chybě naší podpoře, aby mohli zajistit opravu.",
"decimal_places_error": "Příliš mnoho desetinných míst",
"edit_node": "Upravit uzel",
"frozen_balance": "Zmrazená bilance",
"invoice_details": "detaily faktury",
"donation_link_details": "Podrobnosti odkazu na darování",
"anonpay_description": "Vygenerujte ${type}. Příjemce může ${method} s jakoukoli podporovanou kryptoměnou a vy obdržíte prostředky v této peněžence.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Bitte vergessen Sie nicht, das Ziel-Tag anzugeben, während Sie die XRP-Transaktion für den Austausch senden",
"exchange_incorrect_current_wallet_for_xmr" : "Wenn Sie XMR von Ihrem Cake Wallet Monero-Guthaben umtauschen möchten, wechseln Sie bitte zuerst zu Ihrer Monero-Wallet.",
"confirmed" : "Bestätigt",
"unconfirmed" : "Unbestätigt",
"confirmed" : "Bestätigter Saldo",
"unconfirmed" : "Unbestätigter Saldo",
"displayable" : "Anzeigebar",
"submit_request" : "Eine Anfrage stellen",
@ -687,6 +687,7 @@
"error_dialog_content": "Hoppla, wir haben einen Fehler.\n\nBitte senden Sie den Absturzbericht an unser Support-Team, um die Anwendung zu verbessern.",
"decimal_places_error": "Zu viele Nachkommastellen",
"edit_node": "Knoten bearbeiten",
"frozen_balance": "Gefrorenes Gleichgewicht",
"invoice_details": "Rechnungs-Details",
"donation_link_details": "Details zum Spendenlink",
"anonpay_description": "Generieren Sie ${type}. Der Empfänger kann ${method} mit jeder unterstützten Kryptowährung verwenden, und Sie erhalten Geld in dieser Brieftasche.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Please dont forget to specify the Destination Tag while sending the XRP transaction for the exchange",
"exchange_incorrect_current_wallet_for_xmr" : "If you want to exchange XMR from your Cake Wallet Monero balance, please switch to your Monero wallet first.",
"confirmed" : "Confirmed",
"unconfirmed" : "Unconfirmed",
"confirmed" : "Confirmed Balance",
"unconfirmed" : "Unconfirmed Balance",
"displayable" : "Displayable",
"submit_request" : "submit a request",
@ -697,6 +697,7 @@
"onion_link": "Onion link",
"decimal_places_error": "Too many decimal places",
"edit_node": "Edit Node",
"frozen_balance": "Frozen Balance",
"settings": "Settings",
"sell_monero_com_alert_content": "Selling Monero is not supported yet",
"error_text_input_below_minimum_limit" : "Amount is less than the minimum",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "No olvide especificar la etiqueta de destino al enviar la transacción XRP para el intercambio",
"exchange_incorrect_current_wallet_for_xmr" : "Si desea intercambiar XMR de su saldo de Cake Wallet Monero, primero cambie a su billetera Monero.",
"confirmed" : "Confirmada",
"unconfirmed" : "Inconfirmado",
"confirmed" : "Saldo confirmado",
"unconfirmed" : "Saldo no confirmado",
"displayable" : "Visualizable",
"submit_request" : "presentar una solicitud",
@ -686,7 +686,8 @@
"do_not_send": "no enviar",
"error_dialog_content": "Vaya, tenemos un error.\n\nEnvíe el informe de bloqueo a nuestro equipo de soporte para mejorar la aplicación.",
"decimal_places_error": "Demasiados lugares decimales",
"edit_node": "Edit Node",
"edit_node": "Editar nodo",
"frozen_balance": "Balance congelado",
"invoice_details": "Detalles de la factura",
"donation_link_details": "Detalles del enlace de donación",
"anonpay_description": "Genera ${type}. El destinatario puede ${method} con cualquier criptomoneda admitida, y recibirá fondos en esta billetera.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Merci de ne pas oublier de spécifier le tag de destination lors de l'envoi de la transaction XRP de l'échange",
"exchange_incorrect_current_wallet_for_xmr" : "Si vous souhaitez échanger des XMR du solde Monero de votre Cake Wallet, merci de sélectionner votre portefeuille (wallet) Monero au préalable.",
"confirmed" : "Confirmé",
"unconfirmed" : "Non confirmé",
"confirmed" : "Solde confirmé",
"unconfirmed" : "Solde non confirmé",
"displayable" : "Visible",
"submit_request" : "soumettre une requête",
@ -687,6 +687,7 @@
"error_dialog_content": "Oups, nous avons rencontré une erreur.\n\nMerci d'envoyer le rapport d'erreur à notre équipe d'assistance afin de nous permettre d'améliorer l'application.",
"decimal_places_error": "Trop de décimales",
"edit_node": "Modifier le nœud",
"frozen_balance": "Équilibre gelé",
"invoice_details": "Détails de la facture",
"donation_link_details": "Détails du lien de don",
"anonpay_description": "Générez ${type}. Le destinataire peut ${method} avec n'importe quelle crypto-monnaie prise en charge, et vous recevrez des fonds dans ce portefeuille (wallet).",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "एक्सचेंज के लिए एक्सआरपी लेनदेन भेजते समय कृपया गंतव्य टैग निर्दिष्ट करना न भूलें",
"exchange_incorrect_current_wallet_for_xmr" : "यदि आप अपने केक वॉलेट मोनेरो बैलेंस से एक्सएमआर का आदान-प्रदान करना चाहते हैं, तो कृपया अपने मोनेरो वॉलेट में जाएं।",
"confirmed" : "की पुष्टि की",
"unconfirmed" : "अपुष्ट",
"confirmed" : "पुष्टि की गई शेष राशिी",
"unconfirmed" : "अपुष्ट शेष राशि",
"displayable" : "प्रदर्शन योग्य",
"submit_request" : "एक अनुरोध सबमिट करें",
@ -687,6 +687,7 @@
"error_dialog_content": "ओह, हमसे कुछ गड़बड़ी हुई है.\n\nएप्लिकेशन को बेहतर बनाने के लिए कृपया क्रैश रिपोर्ट हमारी सहायता टीम को भेजें।",
"decimal_places_error": "बहुत अधिक दशमलव स्थान",
"edit_node": "नोड संपादित करें",
"frozen_balance": "जमे हुए संतुलन",
"invoice_details": "चालान विवरण",
"donation_link_details": "दान लिंक विवरण",
"anonpay_description": "${type} उत्पन्न करें। प्राप्तकर्ता किसी भी समर्थित क्रिप्टोकरेंसी के साथ ${method} कर सकता है, और आपको इस वॉलेट में धन प्राप्त होगा।",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Molimo ne zaboravite navesti odredišnu oznaku prilikom slanja XRP transakcije na razmjenu",
"exchange_incorrect_current_wallet_for_xmr" : "Ako želite razmijeniti XMR s vlastitog Monero računa na Cake Wallet novčaniku, molimo prvo se prebacite na svoj Monero novčanik.",
"confirmed" : "Potvrđeno",
"unconfirmed" : "Nepotvrđeno",
"confirmed" : "Potvrđeno stanje",
"unconfirmed" : "Nepotvrđeno stanje",
"displayable" : "Dostupno za prikaz",
"submit_request" : "podnesi zahtjev",
@ -687,6 +687,7 @@
"error_dialog_content": "Ups, imamo grešku.\n\nPošaljite izvješće o padu našem timu za podršku kako bismo poboljšali aplikaciju.",
"decimal_places_error": "Previše decimalnih mjesta",
"edit_node": "Uredi čvor",
"frozen_balance": "Zamrznuti saldo",
"invoice_details": "Podaci o fakturi",
"donation_link_details": "Detalji veza za donacije",
"anonpay_description": "Generiraj ${type}. Primatelj može ${method} s bilo kojom podržanom kriptovalutom, a vi ćete primiti sredstva u ovaj novčanik.",

View file

@ -466,8 +466,8 @@
"xrp_extra_info" : "Jangan lupa untuk menentukan Tag Tujuan saat mengirim transaksi XRP untuk pertukaran",
"exchange_incorrect_current_wallet_for_xmr" : "Jika Anda ingin menukar XMR dari saldo Monero Cake Wallet Anda, silakan beralih ke dompet Monero Anda terlebih dahulu.",
"confirmed" : "Dikonfirmasi",
"unconfirmed" : "Tidak dikonfirmasi",
"confirmed" : "Saldo Terkonfirmasi",
"unconfirmed" : "Saldo Belum Dikonfirmasi",
"displayable" : "Dapat ditampilkan",
"submit_request" : "kirim permintaan",
@ -669,6 +669,7 @@
"contact_list_wallets": "Dompet Saya",
"decimal_places_error": "Terlalu banyak tempat desimal",
"edit_node": "Sunting Node",
"frozen_balance": "Saldo Beku",
"invoice_details": "Detail faktur",
"donation_link_details": "Detail tautan donasi",
"anonpay_description": "Hasilkan ${type}. Penerima dapat ${method} dengan cryptocurrency apa pun yang didukung, dan Anda akan menerima dana di dompet ini.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Gentilmente ricorda di indicare il Tag di Destinazione quando invii una transazione XRP per lo scambio",
"exchange_incorrect_current_wallet_for_xmr" : "Se vuoi scambiare XMR dal tuo saldo Cake Wallet Monero, gentilmente passa al tuo portafoglio Monero.",
"confirmed" : "Confermato",
"unconfirmed" : "Non confermato",
"confirmed" : "Saldo confermato",
"unconfirmed" : "Saldo non confermato",
"displayable" : "Visualizzabile",
"submit_request" : "invia una richiesta",
@ -687,6 +687,7 @@
"error_dialog_content": "Spiacenti, abbiamo riscontrato un errore.\n\nSi prega di inviare il rapporto sull'arresto anomalo al nostro team di supporto per migliorare l'applicazione.",
"decimal_places_error": "Troppe cifre decimali",
"edit_node": "Modifica nodo",
"frozen_balance": "Equilibrio congelato",
"invoice_details": "Dettagli della fattura",
"donation_link_details": "Dettagli del collegamento alla donazione",
"anonpay_description": "Genera ${type}. Il destinatario può ${method} con qualsiasi criptovaluta supportata e riceverai fondi in questo portafoglio.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "取引所のXRPトランザクションを送信するときに、宛先タグを指定することを忘れないでください",
"exchange_incorrect_current_wallet_for_xmr" : "Cake Wallet Moneroの残高からXMRを交換する場合は、最初にMoneroウォレットに切り替えてください。",
"confirmed" : "確認済み",
"unconfirmed" : "未確認",
"confirmed" : "確認済み残高",
"unconfirmed" : "残高未確認",
"displayable" : "表示可能",
"submit_request" : "リクエストを送信する",
@ -687,6 +687,7 @@
"error_dialog_content": "エラーが発生しました。\n\nアプリケーションを改善するために、クラッシュ レポートをサポート チームに送信してください。",
"decimal_places_error": "小数点以下の桁数が多すぎる",
"edit_node": "ノードを編集",
"frozen_balance": "冷凍残高",
"invoice_details": "請求の詳細",
"donation_link_details": "寄付リンクの詳細",
"anonpay_description": "${type} を生成します。受取人はサポートされている任意の暗号通貨で ${method} でき、あなたはこのウォレットで資金を受け取ります。",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "교환을 위해 XRP 트랜잭션을 보내는 동안 대상 태그를 지정하는 것을 잊지 마십시오",
"exchange_incorrect_current_wallet_for_xmr" : "Cake Wallet Monero 잔액에서 XMR을 교환하려면 먼저 Monero 지갑으로 전환하십시오.",
"confirmed" : "확인",
"unconfirmed" : "확인",
"confirmed" : "확인된 잔액",
"unconfirmed" : "확인되지 않은 잔액",
"displayable" : "표시 가능",
"submit_request" : "요청을 제출",
@ -687,6 +687,7 @@
"error_dialog_content": "죄송합니다. 오류가 발생했습니다.\n\n응용 프로그램을 개선하려면 지원 팀에 충돌 보고서를 보내주십시오.",
"decimal_places_error": "소수점 이하 자릿수가 너무 많습니다.",
"edit_node": "노드 편집",
"frozen_balance": "얼어붙은 균형",
"invoice_details": "인보이스 세부정보",
"donation_link_details": "기부 링크 세부정보",
"anonpay_description": "${type} 생성. 수신자는 지원되는 모든 암호화폐로 ${method}할 수 있으며 이 지갑에서 자금을 받게 됩니다.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "လဲလှယ်မှုအတွက် XRP ငွေလွှဲပို့နေစဉ် Destination Tag ကို သတ်မှတ်ရန် မမေ့ပါနှင့်",
"exchange_incorrect_current_wallet_for_xmr" : "သင်၏ Cake Wallet Monero လက်ကျန်မှ XMR ကိုလဲလှယ်လိုပါက၊ သင်၏ Monero ပိုက်ဆံအိတ်သို့ ဦးစွာပြောင်းပါ။",
"confirmed" : "အတည်ပြုခဲ့သည်။",
"unconfirmed" : "အတည်မပြုနိုင်ပါ။",
"confirmed" : "အတည်ပြုထားသော လက်ကျန်ငွေ",
"unconfirmed" : "အတည်မပြုနိုင်သော လက်ကျန်ငွေ",
"displayable" : "ပြသနိုင်သည်။",
"submit_request" : "တောင်းဆိုချက်တစ်ခုတင်ပြပါ။",
@ -687,6 +687,7 @@
"error_dialog_content": "အိုး၊ ကျွန်ုပ်တို့တွင် အမှားအယွင်းအချို့ရှိသည်။\n\nအပလီကေးရှင်းကို ပိုမိုကောင်းမွန်စေရန်အတွက် ပျက်စီးမှုအစီရင်ခံစာကို ကျွန်ုပ်တို့၏ပံ့ပိုးကူညီရေးအဖွဲ့ထံ ပေးပို့ပါ။",
"decimal_places_error": "ဒဿမနေရာများ များလွန်းသည်။",
"edit_node": "Node ကို တည်းဖြတ်ပါ။",
"frozen_balance": "ေးခဲမှူ",
"invoice_details": "ပြေစာအသေးစိတ်",
"donation_link_details": "လှူဒါန်းရန်လင့်ခ်အသေးစိတ်",
"anonpay_description": "${type} ကို ဖန်တီးပါ။ လက်ခံသူက ${method} ကို ပံ့ပိုးပေးထားသည့် cryptocurrency တစ်ခုခုဖြင့် လုပ်ဆောင်နိုင်ပြီး၊ သင်သည် ဤပိုက်ဆံအိတ်တွင် ရံပုံငွေများ ရရှိမည်ဖြစ်သည်။",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Vergeet niet om de Destination Tag op te geven tijdens het verzenden van de XRP-transactie voor de uitwisseling",
"exchange_incorrect_current_wallet_for_xmr" : "Als u XMR wilt omwisselen van uw Cake Wallet Monero-saldo, moet u eerst overschakelen naar uw Monero-portemonnee.",
"confirmed" : "Bevestigd",
"unconfirmed" : "Niet bevestigd",
"confirmed" : "Bevestigd saldo",
"unconfirmed" : "Onbevestigd saldo",
"displayable" : "Weer te geven",
"submit_request" : "een verzoek indienen",
@ -687,6 +687,7 @@
"error_dialog_content": "Oeps, er is een fout opgetreden.\n\nStuur het crashrapport naar ons ondersteuningsteam om de applicatie te verbeteren.",
"decimal_places_error": "Te veel decimalen",
"edit_node": "Knooppunt bewerken",
"frozen_balance": "Bevroren saldo",
"invoice_details": "Factuurgegevens",
"donation_link_details": "Details van de donatielink",
"anonpay_description": "Genereer ${type}. De ontvanger kan ${method} gebruiken met elke ondersteunde cryptocurrency en u ontvangt geld in deze portemonnee",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Nie zapomnij podać tagu docelowego podczas wysyłania transakcji XRP do wymiany",
"exchange_incorrect_current_wallet_for_xmr" : "Jeśli chcesz wymienić XMR z salda Cake Wallet Monero, najpierw przełącz się na portfel Monero.",
"confirmed" : "Potwierdzony",
"unconfirmed" : "Niepotwierdzony",
"confirmed" : "Potwierdzone saldo",
"unconfirmed" : "Niepotwierdzone saldo",
"displayable" : "Wyświetlane",
"submit_request" : "Złóż wniosek",
@ -687,6 +687,7 @@
"error_dialog_content": "Ups, wystąpił błąd.\n\nPrześlij raport o awarii do naszego zespołu wsparcia, aby ulepszyć aplikację.",
"decimal_places_error": "Za dużo miejsc dziesiętnych",
"edit_node": "Edytuj węzeł",
"frozen_balance": "Zamrożona równowaga",
"invoice_details": "Dane do faktury",
"donation_link_details": "Szczegóły linku darowizny",
"anonpay_description": "Wygeneruj ${type}. Odbiorca może ${method} z dowolną obsługiwaną kryptowalutą, a Ty otrzymasz środki w tym portfelu.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Não se esqueça de especificar a etiqueta de destino ao enviar a transação XRP para a troca",
"exchange_incorrect_current_wallet_for_xmr" : "Se você deseja trocar o XMR de seu saldo da Carteira Monero Cake, troque primeiro para sua carteira Monero.",
"confirmed" : "Confirmada",
"unconfirmed" : "Não confirmado",
"confirmed" : "Saldo Confirmado",
"unconfirmed" : "Saldo não confirmado",
"displayable" : "Exibível",
"submit_request" : "enviar um pedido",
@ -686,6 +686,7 @@
"error_dialog_content": "Ops, houve algum erro.\n\nPor favor, envie o relatório de falha para nossa equipe de suporte para melhorar o aplicativo.",
"decimal_places_error": "Muitas casas decimais",
"edit_node": "Editar nó",
"frozen_balance": "Saldo Congelado",
"invoice_details": "Detalhes da fatura",
"donation_link_details": "Detalhes do link de doação",
"anonpay_description": "Gere ${type}. O destinatário pode ${method} com qualquer criptomoeda suportada e você receberá fundos nesta carteira.",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Не забудьте указать целевой тег при отправке транзакции XRP для обмена",
"exchange_incorrect_current_wallet_for_xmr" : "Если вы хотите обменять XMR со своего баланса Monero в Cake Wallet, сначала переключитесь на свой кошелек Monero.",
"confirmed" : "Подтверждено",
"unconfirmed" : "Неподтвержденный",
"confirmed" : "Подтвержденный баланс",
"unconfirmed" : "Неподтвержденный баланс",
"displayable" : "Отображаемый",
"submit_request" : "отправить запрос",
@ -687,6 +687,7 @@
"error_dialog_content": "Ой, у нас какая-то ошибка.\n\nПожалуйста, отправьте отчет о сбое в нашу службу поддержки, чтобы сделать приложение лучше.",
"decimal_places_error": "Слишком много десятичных знаков",
"edit_node": "Редактировать узел",
"frozen_balance": "Замороженный баланс",
"invoice_details": "Детали счета",
"donation_link_details": "Информация о ссылке для пожертвований",
"anonpay_description": "Создайте ${type}. Получатель может использовать ${method} с любой поддерживаемой криптовалютой, и вы получите средства на этот кошелек.",

View file

@ -477,8 +477,8 @@
"xrp_extra_info": "โปรดอย่าลืมระบุ Destination Tag ในขณะที่ส่งธุรกรรม XRP สำหรับการแลกเปลี่ยน",
"exchange_incorrect_current_wallet_for_xmr" : "หากคุณต้องการแลกเปลี่ยน XMR จากยอดคงเหลือ Monero ใน Cake Wallet ของคุณ กรุณาเปลี่ยนเป็นกระเป๋า Monero ก่อน",
"confirmed" : "ได้รับการยืนยัน",
"unconfirmed" : "ยังไม่ได้รับการยืนยัน",
"confirmed" : "ยอดคงเหลือที่ยืนยันแล้ว",
"unconfirmed" : "ยอดคงเหลือที่ไม่ได้รับการยืนยัน",
"displayable" : "สามารถแสดงได้",
"submit_request" : "ส่งคำขอ",
@ -685,6 +685,7 @@
"error_dialog_content": "อ๊ะ เราพบข้อผิดพลาดบางอย่าง\n\nโปรดส่งรายงานข้อขัดข้องไปยังทีมสนับสนุนของเราเพื่อปรับปรุงแอปพลิเคชันให้ดียิ่งขึ้น",
"decimal_places_error": "ทศนิยมมากเกินไป",
"edit_node": "แก้ไขโหนด",
"frozen_balance": "ยอดคงเหลือแช่แข็ง",
"invoice_details": "รายละเอียดใบแจ้งหนี้",
"donation_link_details": "รายละเอียดลิงค์บริจาค",
"anonpay_description": "สร้าง ${type} ผู้รับสามารถ ${method} ด้วยสกุลเงินดิจิทัลที่รองรับ และคุณจะได้รับเงินในกระเป๋าสตางค์นี้",

View file

@ -479,8 +479,8 @@
"xrp_extra_info" : "Lütfen takas için XRP işlemi gönderirken Hedef Etiketi (Destination Tag) belirtmeyi unutmayın",
"exchange_incorrect_current_wallet_for_xmr" : "Cake Wallet'daki Monero bakiyenizi kullanarak takas yapmak istiyorsan, lütfen önce Monero cüzdanına geç.",
"confirmed" : "Onaylı",
"unconfirmed" : "Onaylanmamış",
"confirmed" : "Onaylanmış Bakiye",
"unconfirmed" : "Onaylanmamış Bakiye",
"displayable" : "Gösterilebilir",
"submit_request" : "talep gönder",
@ -687,6 +687,7 @@
"error_dialog_content": "Hay aksi, bir hatamız var.\n\nUygulamayı daha iyi hale getirmek için lütfen kilitlenme raporunu destek ekibimize gönderin.",
"decimal_places_error": "Çok fazla ondalık basamak",
"edit_node": "Düğümü Düzenle",
"frozen_balance": "Dondurulmuş Bakiye",
"invoice_details": "fatura detayları",
"donation_link_details": "Bağış bağlantısı ayrıntıları",
"anonpay_description": "${type} oluşturun. Alıcı, desteklenen herhangi bir kripto para birimi ile ${method} yapabilir ve bu cüzdanda para alırsınız.",

View file

@ -478,8 +478,8 @@
"xrp_extra_info" : "Будь ласка, не забудьте вказати тег призначення під час надсилання XRP-транзакції для обміну",
"exchange_incorrect_current_wallet_for_xmr" : "Якщо ви хочете обміняти XMR із вашого балансу Cake Wallet Monero, спочатку перейдіть на свій гаманець Monero.",
"confirmed" : "Підтверджено",
"unconfirmed" : "Непідтверджений",
"confirmed" : "Підтверджений баланс",
"unconfirmed" : "Непідтверджений баланс",
"displayable" : "Відображуваний",
"submit_request" : "надіслати запит",
@ -686,6 +686,7 @@
"error_dialog_content": "На жаль, ми отримали помилку.\n\nБудь ласка, надішліть звіт про збій нашій команді підтримки, щоб покращити додаток.",
"decimal_places_error": "Забагато знаків після коми",
"edit_node": "Редагувати вузол",
"frozen_balance": "Заморожений баланс",
"invoice_details": "Реквізити рахунку-фактури",
"donation_link_details": "Деталі посилання для пожертв",
"anonpay_description": "Згенерувати ${type}. Одержувач може ${method} будь-якою підтримуваною криптовалютою, і ви отримаєте кошти на цей гаманець.",

View file

@ -481,8 +481,8 @@
"xrp_extra_info" : "ایکسچینج کے لیے XRP ٹرانزیکشن بھیجتے وقت ڈیسٹینیشن ٹیگ بتانا نہ بھولیں۔",
"exchange_incorrect_current_wallet_for_xmr" : "اگر آپ اپنے Cake والیٹ Monero بیلنس سے XMR کا تبادلہ کرنا چاہتے ہیں، تو براہ کرم پہلے اپنے Monero والیٹ پر جائیں۔",
"confirmed" : "تصدیق شدہ",
"unconfirmed" : "غیر تصدیق شدہ",
"confirmed" : "تصدیق شدہ بیلنس",
"unconfirmed" : "غیر تصدیق شدہ بیلنس",
"displayable" : "قابل نمائش",
"submit_request" : "درخواست بھیج دو",
@ -688,6 +688,7 @@
"error_dialog_content" : "افوہ، ہمیں کچھ خرابی ملی۔\n\nایپلی کیشن کو بہتر بنانے کے لیے براہ کرم کریش رپورٹ ہماری سپورٹ ٹیم کو بھیجیں۔",
"decimal_places_error": "بہت زیادہ اعشاریہ جگہیں۔",
"edit_node": "نوڈ میں ترمیم کریں۔",
"frozen_balance": "منجمد بیلنس",
"invoice_details": "رسید کی تفصیلات",
"donation_link_details": "عطیہ کے لنک کی تفصیلات",
"anonpay_description": "${type} بنائیں۔ وصول کنندہ کسی بھی تعاون یافتہ کرپٹو کرنسی کے ساتھ ${method} کرسکتا ہے، اور آپ کو اس بٹوے میں فنڈز موصول ہوں گے۔",

View file

@ -478,8 +478,8 @@
"xrp_extra_info" : "发送用于交换的XRP交易时请不要忘记指定目标Tag",
"exchange_incorrect_current_wallet_for_xmr" : "如果要从Cake Wallet Monero余额中兑换XMR请先切换到Monero钱包。",
"confirmed" : "确认",
"unconfirmed" : "未经证实",
"confirmed" : "确认余额",
"unconfirmed" : "未确认余额",
"displayable" : "可显示",
"submit_request" : "提交请求",
@ -686,6 +686,7 @@
"error_dialog_content": "糟糕,我们遇到了一些错误。\n\n请将崩溃报告发送给我们的支持团队以改进应用程序。",
"decimal_places_error": "小数位太多",
"edit_node": "编辑节点",
"frozen_balance": "冻结余额",
"invoice_details": "发票明细",
"donation_link_details": "捐赠链接详情",
"anonpay_description": "生成 ${type}。收款人可以使用任何受支持的加密货币 ${method},您将在此钱包中收到资金。",