mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 03:29:36 +00:00
Cw 830 coin control getting cleared (#1825)
* init commit * add select all button * localisation all coins * fix isSending and isFrozen state updates * fix: clean up electrum UTXOs * ui fixes * address the review comments[skip ci] * remove onPopInvoked[skip ci] --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>
This commit is contained in:
parent
4ca50b5e63
commit
9cd69c4ba3
40 changed files with 402 additions and 125 deletions
|
@ -106,6 +106,15 @@ class BitcoinWalletService extends WalletService<
|
||||||
final walletInfo = walletInfoSource.values
|
final walletInfo = walletInfoSource.values
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||||
await walletInfoSource.delete(walletInfo.key);
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
|
|
||||||
|
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||||
|
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||||
|
|
||||||
|
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||||
|
|
||||||
|
if (keysToDelete.isNotEmpty) {
|
||||||
|
await unspentCoinsInfoSource.deleteAll(keysToDelete);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -304,6 +304,7 @@ abstract class ElectrumWalletBase
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
await walletAddresses.init();
|
await walletAddresses.init();
|
||||||
await transactionHistory.init();
|
await transactionHistory.init();
|
||||||
|
await cleanUpDuplicateUnspentCoins();
|
||||||
await save();
|
await save();
|
||||||
|
|
||||||
_autoSaveTimer =
|
_autoSaveTimer =
|
||||||
|
@ -1379,10 +1380,11 @@ abstract class ElectrumWalletBase
|
||||||
}));
|
}));
|
||||||
|
|
||||||
unspentCoins = updatedUnspentCoins;
|
unspentCoins = updatedUnspentCoins;
|
||||||
|
|
||||||
|
final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id);
|
||||||
|
|
||||||
if (unspentCoinsInfo.length != updatedUnspentCoins.length) {
|
if (currentWalletUnspentCoins.length != updatedUnspentCoins.length) {
|
||||||
unspentCoins.forEach((coin) => addCoinInfo(coin));
|
unspentCoins.forEach((coin) => addCoinInfo(coin));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateCoins(unspentCoins);
|
await updateCoins(unspentCoins);
|
||||||
|
@ -1408,6 +1410,7 @@ abstract class ElectrumWalletBase
|
||||||
coin.isFrozen = coinInfo.isFrozen;
|
coin.isFrozen = coinInfo.isFrozen;
|
||||||
coin.isSending = coinInfo.isSending;
|
coin.isSending = coinInfo.isSending;
|
||||||
coin.note = coinInfo.note;
|
coin.note = coinInfo.note;
|
||||||
|
|
||||||
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
if (coin.bitcoinAddressRecord is! BitcoinSilentPaymentAddressRecord)
|
||||||
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
coin.bitcoinAddressRecord.balance += coinInfo.value;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1445,20 +1448,27 @@ abstract class ElectrumWalletBase
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> addCoinInfo(BitcoinUnspent coin) async {
|
Future<void> addCoinInfo(BitcoinUnspent coin) async {
|
||||||
final newInfo = UnspentCoinsInfo(
|
|
||||||
walletId: id,
|
|
||||||
hash: coin.hash,
|
|
||||||
isFrozen: coin.isFrozen,
|
|
||||||
isSending: coin.isSending,
|
|
||||||
noteRaw: coin.note,
|
|
||||||
address: coin.bitcoinAddressRecord.address,
|
|
||||||
value: coin.value,
|
|
||||||
vout: coin.vout,
|
|
||||||
isChange: coin.isChange,
|
|
||||||
isSilentPayment: coin is BitcoinSilentPaymentsUnspent,
|
|
||||||
);
|
|
||||||
|
|
||||||
await unspentCoinsInfo.add(newInfo);
|
// Check if the coin is already in the unspentCoinsInfo for the wallet
|
||||||
|
final existingCoinInfo = unspentCoinsInfo.values.firstWhereOrNull(
|
||||||
|
(element) => element.walletId == walletInfo.id && element == coin);
|
||||||
|
|
||||||
|
if (existingCoinInfo == null) {
|
||||||
|
final newInfo = UnspentCoinsInfo(
|
||||||
|
walletId: id,
|
||||||
|
hash: coin.hash,
|
||||||
|
isFrozen: coin.isFrozen,
|
||||||
|
isSending: coin.isSending,
|
||||||
|
noteRaw: coin.note,
|
||||||
|
address: coin.bitcoinAddressRecord.address,
|
||||||
|
value: coin.value,
|
||||||
|
vout: coin.vout,
|
||||||
|
isChange: coin.isChange,
|
||||||
|
isSilentPayment: coin is BitcoinSilentPaymentsUnspent,
|
||||||
|
);
|
||||||
|
|
||||||
|
await unspentCoinsInfo.add(newInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _refreshUnspentCoinsInfo() async {
|
Future<void> _refreshUnspentCoinsInfo() async {
|
||||||
|
@ -1486,6 +1496,23 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> cleanUpDuplicateUnspentCoins() async {
|
||||||
|
final currentWalletUnspentCoins = unspentCoinsInfo.values.where((element) => element.walletId == id);
|
||||||
|
final Map<String, UnspentCoinsInfo> uniqueUnspentCoins = {};
|
||||||
|
final List<dynamic> duplicateKeys = [];
|
||||||
|
|
||||||
|
for (final unspentCoin in currentWalletUnspentCoins) {
|
||||||
|
final key = '${unspentCoin.hash}:${unspentCoin.vout}';
|
||||||
|
if (!uniqueUnspentCoins.containsKey(key)) {
|
||||||
|
uniqueUnspentCoins[key] = unspentCoin;
|
||||||
|
} else {
|
||||||
|
duplicateKeys.add(unspentCoin.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicateKeys.isNotEmpty) await unspentCoinsInfo.deleteAll(duplicateKeys);
|
||||||
|
}
|
||||||
|
|
||||||
int transactionVSize(String transactionHex) => BtcTransaction.fromRaw(transactionHex).getVSize();
|
int transactionVSize(String transactionHex) => BtcTransaction.fromRaw(transactionHex).getVSize();
|
||||||
|
|
||||||
Future<String?> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
Future<String?> canReplaceByFee(ElectrumTransactionInfo tx) async {
|
||||||
|
|
|
@ -126,6 +126,15 @@ class LitecoinWalletService extends WalletService<
|
||||||
mwebdLogs.deleteSync();
|
mwebdLogs.deleteSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||||
|
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||||
|
|
||||||
|
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||||
|
|
||||||
|
if (keysToDelete.isNotEmpty) {
|
||||||
|
await unspentCoinsInfoSource.deleteAll(keysToDelete);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -85,6 +85,15 @@ class BitcoinCashWalletService extends WalletService<
|
||||||
final walletInfo = walletInfoSource.values
|
final walletInfo = walletInfoSource.values
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||||
await walletInfoSource.delete(walletInfo.key);
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
|
|
||||||
|
final unspentCoinsToDelete = unspentCoinsInfoSource.values.where(
|
||||||
|
(unspentCoin) => unspentCoin.walletId == walletInfo.id).toList();
|
||||||
|
|
||||||
|
final keysToDelete = unspentCoinsToDelete.map((unspentCoin) => unspentCoin.key).toList();
|
||||||
|
|
||||||
|
if (keysToDelete.isNotEmpty) {
|
||||||
|
await unspentCoinsInfoSource.deleteAll(keysToDelete);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
import 'package:cw_core/hive_type_ids.dart';
|
import 'package:cw_core/hive_type_ids.dart';
|
||||||
|
import 'package:cw_core/unspent_comparable_mixin.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
part 'unspent_coins_info.g.dart';
|
part 'unspent_coins_info.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: UnspentCoinsInfo.typeId)
|
@HiveType(typeId: UnspentCoinsInfo.typeId)
|
||||||
class UnspentCoinsInfo extends HiveObject {
|
class UnspentCoinsInfo extends HiveObject with UnspentComparable {
|
||||||
UnspentCoinsInfo({
|
UnspentCoinsInfo({
|
||||||
required this.walletId,
|
required this.walletId,
|
||||||
required this.hash,
|
required this.hash,
|
||||||
|
|
27
cw_core/lib/unspent_comparable_mixin.dart
Normal file
27
cw_core/lib/unspent_comparable_mixin.dart
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
mixin UnspentComparable {
|
||||||
|
String get address;
|
||||||
|
|
||||||
|
String get hash;
|
||||||
|
|
||||||
|
int get value;
|
||||||
|
|
||||||
|
int get vout;
|
||||||
|
|
||||||
|
String? get keyImage;
|
||||||
|
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (identical(this, other)) return true;
|
||||||
|
|
||||||
|
return other is UnspentComparable &&
|
||||||
|
other.hash == hash &&
|
||||||
|
other.address == address &&
|
||||||
|
other.value == value &&
|
||||||
|
other.vout == vout &&
|
||||||
|
other.keyImage == keyImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return Object.hash(address, hash, value, vout, keyImage);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
class Unspent {
|
import 'package:cw_core/unspent_comparable_mixin.dart';
|
||||||
|
|
||||||
|
class Unspent with UnspentComparable {
|
||||||
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
|
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
|
||||||
: isSending = true,
|
: isSending = true,
|
||||||
isFrozen = false,
|
isFrozen = false,
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart';
|
import 'package:cake_wallet/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_no_action.dart.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||||
|
import 'package:cake_wallet/themes/extensions/cake_text_theme.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_list_view_model.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
class UnspentCoinsListPage extends BasePage {
|
class UnspentCoinsListPage extends BasePage {
|
||||||
UnspentCoinsListPage({required this.unspentCoinsListViewModel});
|
UnspentCoinsListPage({required this.unspentCoinsListViewModel});
|
||||||
|
@ -15,16 +16,53 @@ class UnspentCoinsListPage extends BasePage {
|
||||||
@override
|
@override
|
||||||
String get title => S.current.unspent_coins_title;
|
String get title => S.current.unspent_coins_title;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget leading(BuildContext context) {
|
||||||
|
return MergeSemantics(
|
||||||
|
child: SizedBox(
|
||||||
|
height: 37,
|
||||||
|
width: 37,
|
||||||
|
child: ButtonTheme(
|
||||||
|
minWidth: double.minPositive,
|
||||||
|
child: Semantics(
|
||||||
|
label: S.of(context).seed_alert_back,
|
||||||
|
child: TextButton(
|
||||||
|
style: ButtonStyle(
|
||||||
|
overlayColor: WidgetStateColor.resolveWith((states) => Colors.transparent),
|
||||||
|
),
|
||||||
|
onPressed: () async => await handleOnPopInvoked(context),
|
||||||
|
child: backButton(context),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||||
|
|
||||||
|
Future<void> handleOnPopInvoked(BuildContext context) async {
|
||||||
|
final hasChanged = unspentCoinsListViewModel.hasAdjustableFieldChanged;
|
||||||
|
if (unspentCoinsListViewModel.items.isEmpty || !hasChanged) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
} else {
|
||||||
|
unspentCoinsListViewModel.setIsDisposing(true);
|
||||||
|
await unspentCoinsListViewModel.dispose();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) => UnspentCoinsListForm(unspentCoinsListViewModel);
|
Widget body(BuildContext context) =>
|
||||||
|
UnspentCoinsListForm(unspentCoinsListViewModel, handleOnPopInvoked);
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnspentCoinsListForm extends StatefulWidget {
|
class UnspentCoinsListForm extends StatefulWidget {
|
||||||
UnspentCoinsListForm(this.unspentCoinsListViewModel);
|
UnspentCoinsListForm(this.unspentCoinsListViewModel, this.handleOnPopInvoked);
|
||||||
|
|
||||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||||
|
final Future<void> Function(BuildContext context) handleOnPopInvoked;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
UnspentCoinsListFormState createState() => UnspentCoinsListFormState(unspentCoinsListViewModel);
|
UnspentCoinsListFormState createState() => UnspentCoinsListFormState(unspentCoinsListViewModel);
|
||||||
|
@ -35,36 +73,126 @@ class UnspentCoinsListFormState extends State<UnspentCoinsListForm> {
|
||||||
|
|
||||||
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
final UnspentCoinsListViewModel unspentCoinsListViewModel;
|
||||||
|
|
||||||
|
late Future<void> _initialization;
|
||||||
|
ReactionDisposer? _disposer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_initialization = unspentCoinsListViewModel.initialSetup();
|
||||||
|
_setupReactions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setupReactions() {
|
||||||
|
_disposer = reaction<bool>(
|
||||||
|
(_) => unspentCoinsListViewModel.isDisposing,
|
||||||
|
(isDisposing) {
|
||||||
|
if (isDisposing) {
|
||||||
|
_showSavingDataAlert();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showSavingDataAlert() {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithNoAction(
|
||||||
|
alertContent: 'Updating, please wait…',
|
||||||
|
alertBarrierDismissible: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_disposer?.call();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return PopScope(
|
||||||
padding: EdgeInsets.fromLTRB(24, 12, 24, 24),
|
canPop: false,
|
||||||
child: Observer(
|
onPopInvokedWithResult: (bool didPop, Object? result) async {
|
||||||
builder: (_) => ListView.separated(
|
if (didPop) return;
|
||||||
itemCount: unspentCoinsListViewModel.items.length,
|
if(mounted)
|
||||||
separatorBuilder: (_, __) => SizedBox(height: 15),
|
await widget.handleOnPopInvoked(context);
|
||||||
itemBuilder: (_, int index) {
|
},
|
||||||
return Observer(builder: (_) {
|
child: FutureBuilder<void>(
|
||||||
final item = unspentCoinsListViewModel.items[index];
|
future: _initialization,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
|
return Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
|
||||||
return GestureDetector(
|
if (snapshot.hasError) return Center(child: Text('Failed to load unspent coins'));
|
||||||
onTap: () => Navigator.of(context).pushNamed(Routes.unspentCoinsDetails,
|
|
||||||
arguments: [item, unspentCoinsListViewModel]),
|
return Container(
|
||||||
child: UnspentCoinsListItem(
|
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||||
note: item.note,
|
child: Observer(
|
||||||
amount: item.amount,
|
builder: (_) => Column(
|
||||||
address: item.address,
|
children: [
|
||||||
isSending: item.isSending,
|
if (unspentCoinsListViewModel.items.isNotEmpty)
|
||||||
isFrozen: item.isFrozen,
|
Row(
|
||||||
isChange: item.isChange,
|
children: [
|
||||||
isSilentPayment: item.isSilentPayment,
|
SizedBox(width: 12),
|
||||||
onCheckBoxTap: item.isFrozen
|
StandardCheckbox(
|
||||||
? null
|
iconColor: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
|
||||||
: () async {
|
value: unspentCoinsListViewModel.isAllSelected,
|
||||||
item.isSending = !item.isSending;
|
onChanged: (value) => unspentCoinsListViewModel.toggleSelectAll(value),
|
||||||
await unspentCoinsListViewModel.saveUnspentCoinInfo(item);
|
),
|
||||||
}));
|
SizedBox(width: 12),
|
||||||
});
|
Text(
|
||||||
})));
|
S.current.all_coins,
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: 15),
|
||||||
|
Expanded(
|
||||||
|
child: unspentCoinsListViewModel.items.isEmpty
|
||||||
|
? Center(child: Text('No unspent coins available\ntry to reconnect',textAlign: TextAlign.center))
|
||||||
|
: ListView.separated(
|
||||||
|
itemCount: unspentCoinsListViewModel.items.length,
|
||||||
|
separatorBuilder: (_, __) => SizedBox(height: 15),
|
||||||
|
itemBuilder: (_, int index) {
|
||||||
|
final item = unspentCoinsListViewModel.items[index];
|
||||||
|
return Observer(
|
||||||
|
builder: (_) => GestureDetector(
|
||||||
|
onTap: () => Navigator.of(context).pushNamed(
|
||||||
|
Routes.unspentCoinsDetails,
|
||||||
|
arguments: [item, unspentCoinsListViewModel],
|
||||||
|
),
|
||||||
|
child: UnspentCoinsListItem(
|
||||||
|
note: item.note,
|
||||||
|
amount: item.amount,
|
||||||
|
address: item.address,
|
||||||
|
isSending: item.isSending,
|
||||||
|
isFrozen: item.isFrozen,
|
||||||
|
isChange: item.isChange,
|
||||||
|
isSilentPayment: item.isSilentPayment,
|
||||||
|
onCheckBoxTap: item.isFrozen
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
item.isSending = !item.isSending;
|
||||||
|
await unspentCoinsListViewModel
|
||||||
|
.saveUnspentCoinInfo(item);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,18 @@ import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
|
||||||
|
|
||||||
class AlertWithNoAction extends BaseAlertDialog {
|
class AlertWithNoAction extends BaseAlertDialog {
|
||||||
AlertWithNoAction({
|
AlertWithNoAction({
|
||||||
required this.alertTitle,
|
this.alertTitle,
|
||||||
required this.alertContent,
|
required this.alertContent,
|
||||||
this.alertBarrierDismissible = true,
|
this.alertBarrierDismissible = true,
|
||||||
Key? key,
|
Key? key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String alertTitle;
|
final String? alertTitle;
|
||||||
final String alertContent;
|
final String alertContent;
|
||||||
final bool alertBarrierDismissible;
|
final bool alertBarrierDismissible;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get titleText => alertTitle;
|
String? get titleText => alertTitle;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get contentText => alertContent;
|
String get contentText => alertContent;
|
||||||
|
@ -26,5 +26,5 @@ class AlertWithNoAction extends BaseAlertDialog {
|
||||||
bool get isBottomDividerExists => false;
|
bool get isBottomDividerExists => false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget actionButtons(BuildContext context) => Container(height: 60);
|
Widget actionButtons(BuildContext context) => Container();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'package:flutter/material.dart';
|
||||||
class BaseAlertDialog extends StatelessWidget {
|
class BaseAlertDialog extends StatelessWidget {
|
||||||
String? get headerText => '';
|
String? get headerText => '';
|
||||||
|
|
||||||
String get titleText => '';
|
String? get titleText => '';
|
||||||
|
|
||||||
String get contentText => '';
|
String get contentText => '';
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class BaseAlertDialog extends StatelessWidget {
|
||||||
|
|
||||||
Widget title(BuildContext context) {
|
Widget title(BuildContext context) {
|
||||||
return Text(
|
return Text(
|
||||||
titleText,
|
titleText!,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
|
@ -191,10 +191,11 @@ class BaseAlertDialog extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (headerText?.isNotEmpty ?? false) headerTitle(context),
|
if (headerText?.isNotEmpty ?? false) headerTitle(context),
|
||||||
|
titleText != null ?
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(24, 20, 24, 0),
|
padding: EdgeInsets.fromLTRB(24, 20, 24, 0),
|
||||||
child: title(context),
|
child: title(context),
|
||||||
),
|
) : SizedBox(height: 16),
|
||||||
isDividerExists
|
isDividerExists
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: EdgeInsets.only(top: 16, bottom: 8),
|
padding: EdgeInsets.only(top: 16, bottom: 8),
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
import 'package:cw_core/unspent_comparable_mixin.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
part 'unspent_coins_item.g.dart';
|
part 'unspent_coins_item.g.dart';
|
||||||
|
|
||||||
class UnspentCoinsItem = UnspentCoinsItemBase with _$UnspentCoinsItem;
|
class UnspentCoinsItem = UnspentCoinsItemBase with _$UnspentCoinsItem;
|
||||||
|
|
||||||
abstract class UnspentCoinsItemBase with Store {
|
abstract class UnspentCoinsItemBase with Store, UnspentComparable {
|
||||||
UnspentCoinsItemBase({
|
UnspentCoinsItemBase({
|
||||||
required this.address,
|
required this.address,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
|
@ -13,7 +14,7 @@ abstract class UnspentCoinsItemBase with Store {
|
||||||
required this.note,
|
required this.note,
|
||||||
required this.isSending,
|
required this.isSending,
|
||||||
required this.isChange,
|
required this.isChange,
|
||||||
required this.amountRaw,
|
required this.value,
|
||||||
required this.vout,
|
required this.vout,
|
||||||
required this.keyImage,
|
required this.keyImage,
|
||||||
required this.isSilentPayment,
|
required this.isSilentPayment,
|
||||||
|
@ -41,7 +42,7 @@ abstract class UnspentCoinsItemBase with Store {
|
||||||
bool isChange;
|
bool isChange;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
int amountRaw;
|
int value;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
int vout;
|
int vout;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
part 'unspent_coins_list_view_model.g.dart';
|
part 'unspent_coins_list_view_model.g.dart';
|
||||||
|
@ -22,55 +23,66 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||||
this.coinTypeToSpendFrom = UnspentCoinType.any,
|
this.coinTypeToSpendFrom = UnspentCoinType.any,
|
||||||
}) : _unspentCoinsInfo = unspentCoinsInfo,
|
}) : _unspentCoinsInfo = unspentCoinsInfo,
|
||||||
_items = ObservableList<UnspentCoinsItem>() {
|
items = ObservableList<UnspentCoinsItem>(),
|
||||||
_updateUnspentCoinsInfo();
|
_originalState = {};
|
||||||
_updateUnspents();
|
|
||||||
}
|
|
||||||
|
|
||||||
WalletBase wallet;
|
final WalletBase wallet;
|
||||||
final Box<UnspentCoinsInfo> _unspentCoinsInfo;
|
final Box<UnspentCoinsInfo> _unspentCoinsInfo;
|
||||||
final UnspentCoinType coinTypeToSpendFrom;
|
final UnspentCoinType coinTypeToSpendFrom;
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
ObservableList<UnspentCoinsItem> _items;
|
ObservableList<UnspentCoinsItem> items;
|
||||||
|
|
||||||
|
final Map<String, Map<String, dynamic>> _originalState;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isDisposing = false;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
ObservableList<UnspentCoinsItem> get items => _items;
|
bool get isAllSelected => items.every((element) => element.isFrozen || element.isSending);
|
||||||
|
|
||||||
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
|
Future<void> initialSetup() async {
|
||||||
try {
|
await _updateUnspents();
|
||||||
final info =
|
_storeOriginalState();
|
||||||
getUnspentCoinInfo(item.hash, item.address, item.amountRaw, item.vout, item.keyImage);
|
}
|
||||||
|
|
||||||
if (info == null) {
|
void _storeOriginalState() {
|
||||||
return;
|
_originalState.clear();
|
||||||
}
|
for (final item in items) {
|
||||||
|
_originalState[item.hash] = {
|
||||||
info.isFrozen = item.isFrozen;
|
'isFrozen': item.isFrozen,
|
||||||
info.isSending = item.isSending;
|
'note': item.note,
|
||||||
info.note = item.note;
|
'isSending': item.isSending,
|
||||||
|
};
|
||||||
await info.save();
|
|
||||||
await _updateUnspents();
|
|
||||||
await wallet.updateBalance();
|
|
||||||
} catch (e) {
|
|
||||||
print(e.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UnspentCoinsInfo? getUnspentCoinInfo(
|
bool _hasAdjustableFieldChanged(UnspentCoinsItem item) {
|
||||||
String hash, String address, int value, int vout, String? keyImage) {
|
final original = _originalState[item.hash];
|
||||||
|
if (original == null) return false;
|
||||||
|
return original['isFrozen'] != item.isFrozen ||
|
||||||
|
original['note'] != item.note ||
|
||||||
|
original['isSending'] != item.isSending;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get hasAdjustableFieldChanged => items.any(_hasAdjustableFieldChanged);
|
||||||
|
|
||||||
|
|
||||||
|
Future<void> saveUnspentCoinInfo(UnspentCoinsItem item) async {
|
||||||
try {
|
try {
|
||||||
return _unspentCoinsInfo.values.firstWhere((element) =>
|
final existingInfo = _unspentCoinsInfo.values
|
||||||
element.walletId == wallet.id &&
|
.firstWhereOrNull((element) => element.walletId == wallet.id && element == item);
|
||||||
element.hash == hash &&
|
if (existingInfo == null) return;
|
||||||
element.address == address &&
|
|
||||||
element.value == value &&
|
existingInfo.isFrozen = item.isFrozen;
|
||||||
element.vout == vout &&
|
existingInfo.isSending = item.isSending;
|
||||||
element.keyImage == keyImage);
|
existingInfo.note = item.note;
|
||||||
|
|
||||||
|
|
||||||
|
await existingInfo.save();
|
||||||
|
_updateUnspentCoinsInfo();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("UnspentCoinsInfo not found for coin: $e");
|
print('Error saving coin info: $e');
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,37 +127,60 @@ abstract class UnspentCoinsListViewModelBase with Store {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
void _updateUnspentCoinsInfo() {
|
void _updateUnspentCoinsInfo() {
|
||||||
_items.clear();
|
items.clear();
|
||||||
|
|
||||||
List<UnspentCoinsItem> unspents = [];
|
final unspents = _getUnspents()
|
||||||
_getUnspents().forEach((Unspent elem) {
|
.map((elem) {
|
||||||
try {
|
try {
|
||||||
final info =
|
final existingItem = _unspentCoinsInfo.values
|
||||||
getUnspentCoinInfo(elem.hash, elem.address, elem.value, elem.vout, elem.keyImage);
|
.firstWhereOrNull((item) => item.walletId == wallet.id && item == elem);
|
||||||
if (info == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unspents.add(UnspentCoinsItem(
|
if (existingItem == null) return null;
|
||||||
address: elem.address,
|
|
||||||
amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}',
|
|
||||||
hash: elem.hash,
|
|
||||||
isFrozen: info.isFrozen,
|
|
||||||
note: info.note,
|
|
||||||
isSending: info.isSending,
|
|
||||||
amountRaw: elem.value,
|
|
||||||
vout: elem.vout,
|
|
||||||
keyImage: elem.keyImage,
|
|
||||||
isChange: elem.isChange,
|
|
||||||
isSilentPayment: info.isSilentPayment ?? false,
|
|
||||||
));
|
|
||||||
} catch (e, s) {
|
|
||||||
print(s);
|
|
||||||
print(e.toString());
|
|
||||||
ExceptionHandler.onError(FlutterErrorDetails(exception: e, stack: s));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_items.addAll(unspents);
|
return UnspentCoinsItem(
|
||||||
|
address: elem.address,
|
||||||
|
amount: '${formatAmountToString(elem.value)} ${wallet.currency.title}',
|
||||||
|
hash: elem.hash,
|
||||||
|
isFrozen: existingItem.isFrozen,
|
||||||
|
note: existingItem.note,
|
||||||
|
isSending: existingItem.isSending,
|
||||||
|
value: elem.value,
|
||||||
|
vout: elem.vout,
|
||||||
|
keyImage: elem.keyImage,
|
||||||
|
isChange: elem.isChange,
|
||||||
|
isSilentPayment: existingItem.isSilentPayment ?? false,
|
||||||
|
);
|
||||||
|
} catch (e, s) {
|
||||||
|
print('Error: $e\nStack: $s');
|
||||||
|
ExceptionHandler.onError(
|
||||||
|
FlutterErrorDetails(exception: e, stack: s),
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.whereType<UnspentCoinsItem>()
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
unspents.sort((a, b) => b.value.compareTo(a.value));
|
||||||
|
|
||||||
|
items.addAll(unspents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void toggleSelectAll(bool value) {
|
||||||
|
for (final item in items) {
|
||||||
|
if (item.isFrozen || item.isSending == value) continue;
|
||||||
|
item.isSending = value;
|
||||||
|
saveUnspentCoinInfo(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void setIsDisposing(bool value) => isDisposing = value;
|
||||||
|
|
||||||
|
@action
|
||||||
|
Future<void> dispose() async {
|
||||||
|
await _updateUnspents();
|
||||||
|
await wallet.updateBalance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "من خلال إنشاء حساب فإنك توافق على",
|
"agree_to": "من خلال إنشاء حساب فإنك توافق على",
|
||||||
"alert_notice": "يلاحظ",
|
"alert_notice": "يلاحظ",
|
||||||
"all": "الكل",
|
"all": "الكل",
|
||||||
|
"all_coins": "كل العملات المعدنية",
|
||||||
"all_trades": "جميع عمليات التداول",
|
"all_trades": "جميع عمليات التداول",
|
||||||
"all_transactions": "كل التحركات المالية",
|
"all_transactions": "كل التحركات المالية",
|
||||||
"alphabetical": "مرتب حسب الحروف الأبجدية",
|
"alphabetical": "مرتب حسب الحروف الأبجدية",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Чрез създаването на акаунт вие се съгласявате с ",
|
"agree_to": "Чрез създаването на акаунт вие се съгласявате с ",
|
||||||
"alert_notice": "Забележете",
|
"alert_notice": "Забележете",
|
||||||
"all": "ALL",
|
"all": "ALL",
|
||||||
|
"all_coins": "Всички монети",
|
||||||
"all_trades": "Всички сделкки",
|
"all_trades": "Всички сделкки",
|
||||||
"all_transactions": "Всички транзакции",
|
"all_transactions": "Всички транзакции",
|
||||||
"alphabetical": "Азбучен ред",
|
"alphabetical": "Азбучен ред",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Vytvořením účtu souhlasíte s ",
|
"agree_to": "Vytvořením účtu souhlasíte s ",
|
||||||
"alert_notice": "Oznámení",
|
"alert_notice": "Oznámení",
|
||||||
"all": "VŠE",
|
"all": "VŠE",
|
||||||
|
"all_coins": "Všechny mince",
|
||||||
"all_trades": "Všechny obchody",
|
"all_trades": "Všechny obchody",
|
||||||
"all_transactions": "Všechny transakce",
|
"all_transactions": "Všechny transakce",
|
||||||
"alphabetical": "Abecední",
|
"alphabetical": "Abecední",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Indem Sie ein Konto erstellen, stimmen Sie den ",
|
"agree_to": "Indem Sie ein Konto erstellen, stimmen Sie den ",
|
||||||
"alert_notice": "Beachten",
|
"alert_notice": "Beachten",
|
||||||
"all": "ALLES",
|
"all": "ALLES",
|
||||||
|
"all_coins": "Alle Münzen",
|
||||||
"all_trades": "Alle Trades",
|
"all_trades": "Alle Trades",
|
||||||
"all_transactions": "Alle Transaktionen",
|
"all_transactions": "Alle Transaktionen",
|
||||||
"alphabetical": "Alphabetisch",
|
"alphabetical": "Alphabetisch",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "By creating account you agree to the ",
|
"agree_to": "By creating account you agree to the ",
|
||||||
"alert_notice": "Notice",
|
"alert_notice": "Notice",
|
||||||
"all": "ALL",
|
"all": "ALL",
|
||||||
|
"all_coins": "All Coins",
|
||||||
"all_trades": "All trades",
|
"all_trades": "All trades",
|
||||||
"all_transactions": "All transactions",
|
"all_transactions": "All transactions",
|
||||||
"alphabetical": "Alphabetical",
|
"alphabetical": "Alphabetical",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Al crear una cuenta, aceptas ",
|
"agree_to": "Al crear una cuenta, aceptas ",
|
||||||
"alert_notice": "Aviso",
|
"alert_notice": "Aviso",
|
||||||
"all": "Todos",
|
"all": "Todos",
|
||||||
|
"all_coins": "Todas las monedas",
|
||||||
"all_trades": "Todos los oficios",
|
"all_trades": "Todos los oficios",
|
||||||
"all_transactions": "Todas las transacciones",
|
"all_transactions": "Todas las transacciones",
|
||||||
"alphabetical": "Alfabético",
|
"alphabetical": "Alfabético",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "En créant un compte, vous acceptez les ",
|
"agree_to": "En créant un compte, vous acceptez les ",
|
||||||
"alert_notice": "Avis",
|
"alert_notice": "Avis",
|
||||||
"all": "TOUT",
|
"all": "TOUT",
|
||||||
|
"all_coins": "Toutes les pièces",
|
||||||
"all_trades": "Tous échanges",
|
"all_trades": "Tous échanges",
|
||||||
"all_transactions": "Toutes transactions",
|
"all_transactions": "Toutes transactions",
|
||||||
"alphabetical": "Alphabétique",
|
"alphabetical": "Alphabétique",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Ta hanyar ƙirƙirar asusu kun yarda da",
|
"agree_to": "Ta hanyar ƙirƙirar asusu kun yarda da",
|
||||||
"alert_notice": "Sanarwa",
|
"alert_notice": "Sanarwa",
|
||||||
"all": "DUK",
|
"all": "DUK",
|
||||||
|
"all_coins": "Duk tsabar kudi",
|
||||||
"all_trades": "Duk ciniki",
|
"all_trades": "Duk ciniki",
|
||||||
"all_transactions": "Dukan Ma'amaloli",
|
"all_transactions": "Dukan Ma'amaloli",
|
||||||
"alphabetical": "Harafi",
|
"alphabetical": "Harafi",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "खाता बनाकर आप इससे सहमत होते हैं ",
|
"agree_to": "खाता बनाकर आप इससे सहमत होते हैं ",
|
||||||
"alert_notice": "सूचना",
|
"alert_notice": "सूचना",
|
||||||
"all": "सब",
|
"all": "सब",
|
||||||
|
"all_coins": "सभी सिक्के",
|
||||||
"all_trades": "सभी व्यापार",
|
"all_trades": "सभी व्यापार",
|
||||||
"all_transactions": "सभी लेन - देन",
|
"all_transactions": "सभी लेन - देन",
|
||||||
"alphabetical": "वर्णमाला",
|
"alphabetical": "वर्णमाला",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Stvaranjem računa pristajete na ",
|
"agree_to": "Stvaranjem računa pristajete na ",
|
||||||
"alert_notice": "Obavijest",
|
"alert_notice": "Obavijest",
|
||||||
"all": "SVE",
|
"all": "SVE",
|
||||||
|
"all_coins": "Sve kovanice",
|
||||||
"all_trades": "Svi obrti",
|
"all_trades": "Svi obrti",
|
||||||
"all_transactions": "Sve transakcije",
|
"all_transactions": "Sve transakcije",
|
||||||
"alphabetical": "Abecedno",
|
"alphabetical": "Abecedno",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Ստեղծելով հաշիվ դուք համաձայնում եք ",
|
"agree_to": "Ստեղծելով հաշիվ դուք համաձայնում եք ",
|
||||||
"alert_notice": "Ծանուցում",
|
"alert_notice": "Ծանուցում",
|
||||||
"all": "Բոլորը",
|
"all": "Բոլորը",
|
||||||
|
"all_coins": "Բոլոր մետաղադրամները",
|
||||||
"all_trades": "Բոլոր գործարքները",
|
"all_trades": "Բոլոր գործարքները",
|
||||||
"all_transactions": "Բոլոր գործառնությունները",
|
"all_transactions": "Բոլոր գործառնությունները",
|
||||||
"alphabetical": "Այբբենական",
|
"alphabetical": "Այբբենական",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Dengan membuat akun Anda setuju dengan ",
|
"agree_to": "Dengan membuat akun Anda setuju dengan ",
|
||||||
"alert_notice": "Melihat",
|
"alert_notice": "Melihat",
|
||||||
"all": "SEMUA",
|
"all": "SEMUA",
|
||||||
|
"all_coins": "Semua koin",
|
||||||
"all_trades": "Semua perdagangan",
|
"all_trades": "Semua perdagangan",
|
||||||
"all_transactions": "Semua transaksi",
|
"all_transactions": "Semua transaksi",
|
||||||
"alphabetical": "Alfabetis",
|
"alphabetical": "Alfabetis",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Creando un account accetti il ",
|
"agree_to": "Creando un account accetti il ",
|
||||||
"alert_notice": "Avviso",
|
"alert_notice": "Avviso",
|
||||||
"all": "TUTTO",
|
"all": "TUTTO",
|
||||||
|
"all_coins": "Tutte le monete",
|
||||||
"all_trades": "Svi obrti",
|
"all_trades": "Svi obrti",
|
||||||
"all_transactions": "Sve transakcije",
|
"all_transactions": "Sve transakcije",
|
||||||
"alphabetical": "Alfabetico",
|
"alphabetical": "Alfabetico",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "アカウントを作成することにより、",
|
"agree_to": "アカウントを作成することにより、",
|
||||||
"alert_notice": "知らせ",
|
"alert_notice": "知らせ",
|
||||||
"all": "すべて",
|
"all": "すべて",
|
||||||
|
"all_coins": "すべてのコイン",
|
||||||
"all_trades": "すべての取引",
|
"all_trades": "すべての取引",
|
||||||
"all_transactions": "全取引",
|
"all_transactions": "全取引",
|
||||||
"alphabetical": "アルファベット順",
|
"alphabetical": "アルファベット順",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "계정을 생성하면 ",
|
"agree_to": "계정을 생성하면 ",
|
||||||
"alert_notice": "알아채다",
|
"alert_notice": "알아채다",
|
||||||
"all": "모든",
|
"all": "모든",
|
||||||
|
"all_coins": "모든 동전",
|
||||||
"all_trades": "A모든 거래",
|
"all_trades": "A모든 거래",
|
||||||
"all_transactions": "모든 거래 창구",
|
"all_transactions": "모든 거래 창구",
|
||||||
"alphabetical": "알파벳순",
|
"alphabetical": "알파벳순",
|
||||||
|
@ -495,8 +496,8 @@
|
||||||
"placeholder_transactions": "거래가 여기에 표시됩니다",
|
"placeholder_transactions": "거래가 여기에 표시됩니다",
|
||||||
"please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.",
|
"please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.",
|
||||||
"please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.",
|
"please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.",
|
||||||
"Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
|
|
||||||
"please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
|
"please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
|
||||||
|
"Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.",
|
||||||
"please_select": "선택 해주세요:",
|
"please_select": "선택 해주세요:",
|
||||||
"please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.",
|
"please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.",
|
||||||
"please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오",
|
"please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "အကောင့်ဖန်တီးခြင်းဖြင့် သင်သည် ဤအရာကို သဘောတူပါသည်။",
|
"agree_to": "အကောင့်ဖန်တီးခြင်းဖြင့် သင်သည် ဤအရာကို သဘောတူပါသည်။",
|
||||||
"alert_notice": "မှတ်သား",
|
"alert_notice": "မှတ်သား",
|
||||||
"all": "အားလုံး",
|
"all": "အားလုံး",
|
||||||
|
"all_coins": "အားလုံးဒင်္ဂါးများ",
|
||||||
"all_trades": "ကုန်သွယ်မှုအားလုံး",
|
"all_trades": "ကုန်သွယ်မှုအားလုံး",
|
||||||
"all_transactions": "အရောင်းအဝယ်အားလုံး",
|
"all_transactions": "အရောင်းအဝယ်အားလုံး",
|
||||||
"alphabetical": "အက္ခရာစဉ်",
|
"alphabetical": "အက္ခရာစဉ်",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Door een account aan te maken gaat u akkoord met de ",
|
"agree_to": "Door een account aan te maken gaat u akkoord met de ",
|
||||||
"alert_notice": "Kennisgeving",
|
"alert_notice": "Kennisgeving",
|
||||||
"all": "ALLE",
|
"all": "ALLE",
|
||||||
|
"all_coins": "Alle munten",
|
||||||
"all_trades": "Alle transacties",
|
"all_trades": "Alle transacties",
|
||||||
"all_transactions": "Alle transacties",
|
"all_transactions": "Alle transacties",
|
||||||
"alphabetical": "Alfabetisch",
|
"alphabetical": "Alfabetisch",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Tworząc konto wyrażasz zgodę na ",
|
"agree_to": "Tworząc konto wyrażasz zgodę na ",
|
||||||
"alert_notice": "Ogłoszenie",
|
"alert_notice": "Ogłoszenie",
|
||||||
"all": "WSZYSTKO",
|
"all": "WSZYSTKO",
|
||||||
|
"all_coins": "Wszystkie monety",
|
||||||
"all_trades": "Wszystkie operacje",
|
"all_trades": "Wszystkie operacje",
|
||||||
"all_transactions": "Wszystkie transakcje",
|
"all_transactions": "Wszystkie transakcje",
|
||||||
"alphabetical": "Alfabetyczny",
|
"alphabetical": "Alfabetyczny",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Ao criar conta você concorda com ",
|
"agree_to": "Ao criar conta você concorda com ",
|
||||||
"alert_notice": "Perceber",
|
"alert_notice": "Perceber",
|
||||||
"all": "TUDO",
|
"all": "TUDO",
|
||||||
|
"all_coins": "Todas as moedas",
|
||||||
"all_trades": "Todas as negociações",
|
"all_trades": "Todas as negociações",
|
||||||
"all_transactions": "Todas as transacções",
|
"all_transactions": "Todas as transacções",
|
||||||
"alphabetical": "alfabética",
|
"alphabetical": "alfabética",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Создавая аккаунт, вы соглашаетесь с ",
|
"agree_to": "Создавая аккаунт, вы соглашаетесь с ",
|
||||||
"alert_notice": "Уведомление",
|
"alert_notice": "Уведомление",
|
||||||
"all": "ВСЕ",
|
"all": "ВСЕ",
|
||||||
|
"all_coins": "Все монеты",
|
||||||
"all_trades": "Все сделки",
|
"all_trades": "Все сделки",
|
||||||
"all_transactions": "Все транзакции",
|
"all_transactions": "Все транзакции",
|
||||||
"alphabetical": "Алфавитный",
|
"alphabetical": "Алфавитный",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "การสร้างบัญชีของคุณยอมรับเงื่อนไขของ",
|
"agree_to": "การสร้างบัญชีของคุณยอมรับเงื่อนไขของ",
|
||||||
"alert_notice": "สังเกต",
|
"alert_notice": "สังเกต",
|
||||||
"all": "ทั้งหมด",
|
"all": "ทั้งหมด",
|
||||||
|
"all_coins": "เหรียญทั้งหมด",
|
||||||
"all_trades": "การซื้อขายทั้งหมด",
|
"all_trades": "การซื้อขายทั้งหมด",
|
||||||
"all_transactions": "การทำธุรกรรมทั้งหมด",
|
"all_transactions": "การทำธุรกรรมทั้งหมด",
|
||||||
"alphabetical": "ตามตัวอักษร",
|
"alphabetical": "ตามตัวอักษร",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Sa pamamagitan ng paggawa ng account sumasang-ayon ka sa ",
|
"agree_to": "Sa pamamagitan ng paggawa ng account sumasang-ayon ka sa ",
|
||||||
"alert_notice": "PAUNAWA",
|
"alert_notice": "PAUNAWA",
|
||||||
"all": "LAHAT",
|
"all": "LAHAT",
|
||||||
|
"all_coins": "Lahat ng mga barya",
|
||||||
"all_trades": "Lahat ng mga trade",
|
"all_trades": "Lahat ng mga trade",
|
||||||
"all_transactions": "Lahat ng mga transaksyon",
|
"all_transactions": "Lahat ng mga transaksyon",
|
||||||
"alphabetical": "Alpabeto",
|
"alphabetical": "Alpabeto",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Hesap oluşturarak bunları kabul etmiş olursunuz ",
|
"agree_to": "Hesap oluşturarak bunları kabul etmiş olursunuz ",
|
||||||
"alert_notice": "Fark etme",
|
"alert_notice": "Fark etme",
|
||||||
"all": "HEPSİ",
|
"all": "HEPSİ",
|
||||||
|
"all_coins": "Tüm Paralar",
|
||||||
"all_trades": "Tüm takaslar",
|
"all_trades": "Tüm takaslar",
|
||||||
"all_transactions": "Tüm transferler",
|
"all_transactions": "Tüm transferler",
|
||||||
"alphabetical": "Alfabetik",
|
"alphabetical": "Alfabetik",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Створюючи обліковий запис, ви погоджуєтеся з ",
|
"agree_to": "Створюючи обліковий запис, ви погоджуєтеся з ",
|
||||||
"alert_notice": "Ув'язнення",
|
"alert_notice": "Ув'язнення",
|
||||||
"all": "ВСЕ",
|
"all": "ВСЕ",
|
||||||
|
"all_coins": "Всі монети",
|
||||||
"all_trades": "Всі операції",
|
"all_trades": "Всі операції",
|
||||||
"all_transactions": "Всі транзакції",
|
"all_transactions": "Всі транзакції",
|
||||||
"alphabetical": "Алфавітний",
|
"alphabetical": "Алфавітний",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "اکاؤنٹ بنا کر آپ اس سے اتفاق کرتے ہیں۔",
|
"agree_to": "اکاؤنٹ بنا کر آپ اس سے اتفاق کرتے ہیں۔",
|
||||||
"alert_notice": "نوٹس",
|
"alert_notice": "نوٹس",
|
||||||
"all": "تمام",
|
"all": "تمام",
|
||||||
|
"all_coins": "تمام سکے",
|
||||||
"all_trades": "تمام تجارت",
|
"all_trades": "تمام تجارت",
|
||||||
"all_transactions": "تمام لین دین",
|
"all_transactions": "تمام لین دین",
|
||||||
"alphabetical": "حروف تہجی کے مطابق",
|
"alphabetical": "حروف تہجی کے مطابق",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Bằng cách tạo tài khoản, bạn đồng ý với ",
|
"agree_to": "Bằng cách tạo tài khoản, bạn đồng ý với ",
|
||||||
"alert_notice": "Để ý",
|
"alert_notice": "Để ý",
|
||||||
"all": "TẤT CẢ",
|
"all": "TẤT CẢ",
|
||||||
|
"all_coins": "Tất cả các đồng tiền",
|
||||||
"all_trades": "Tất cả giao dịch",
|
"all_trades": "Tất cả giao dịch",
|
||||||
"all_transactions": "Tất cả giao dịch",
|
"all_transactions": "Tất cả giao dịch",
|
||||||
"alphabetical": "Theo thứ tự chữ cái",
|
"alphabetical": "Theo thứ tự chữ cái",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "Tẹ́ ẹ bá dá àkáǹtì ẹ jọ rò ",
|
"agree_to": "Tẹ́ ẹ bá dá àkáǹtì ẹ jọ rò ",
|
||||||
"alert_notice": "Akiyesi",
|
"alert_notice": "Akiyesi",
|
||||||
"all": "Gbogbo",
|
"all": "Gbogbo",
|
||||||
|
"all_coins": "Gbogbo awọn owó",
|
||||||
"all_trades": "Gbogbo àwọn pàṣípààrọ̀",
|
"all_trades": "Gbogbo àwọn pàṣípààrọ̀",
|
||||||
"all_transactions": "Gbogbo àwọn àránṣẹ́",
|
"all_transactions": "Gbogbo àwọn àránṣẹ́",
|
||||||
"alphabetical": "Labidibi",
|
"alphabetical": "Labidibi",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"agree_to": "创建账户即表示您同意 ",
|
"agree_to": "创建账户即表示您同意 ",
|
||||||
"alert_notice": "注意",
|
"alert_notice": "注意",
|
||||||
"all": "全部",
|
"all": "全部",
|
||||||
|
"all_coins": "所有硬币",
|
||||||
"all_trades": "所有的变化",
|
"all_trades": "所有的变化",
|
||||||
"all_transactions": "所有交易",
|
"all_transactions": "所有交易",
|
||||||
"alphabetical": "按字母顺序",
|
"alphabetical": "按字母顺序",
|
||||||
|
|
Loading…
Reference in a new issue