Merge branch 'main' of github.com:cake-tech/cake_wallet into hv

This commit is contained in:
M 2022-03-18 11:55:49 +01:00
commit 3354a06649
40 changed files with 1196 additions and 641 deletions

59
assets/faq/faq_fr.json Normal file
View file

@ -0,0 +1,59 @@
[
{
"question" : "Quelle est la différence entre le solde disponible et le solde total ?",
"answer" : "Lorsque vous faites une transaction ou recevez des Monero, la transaction doit être confirmée. Au bout d'environ 20 minutes votre solde disponible devrait se mettre à jour !\nParfois, lorsque vous envoyez des Monero, votre solde disponible peut diminuer d'un montant supérieur à celui que vous avez envoyé. C'est normal, c'est une mesure destinée à augmenter votre intimité. Votre solde total devrait redevenir normal après environ 20 minutes.\n"
},
{
"question" : "Comment envoyer des Monero vers une plateforme d'échange qui nécessite un ID de paiement ?",
"answer" : "Appuyez sur le bouton envoyer de l'écran du Wallet. Ensuite, copiez l'adresse de dépôt de la plateforme d'échange et collez là dans le champ adresse. Puis, copiez l'ID de paiement fourni par la plateforme d'échange et copiez le dans le champ ID de paiement. Enfin, entrez le montant que vous souhaitez envoyer et vous êtes fin prêt !\n"
},
{
"question" : "Que faire si j'oublie d'entrer l'ID de paiement quand j'envoie des Monero vers une plateforme d'échange ?",
"answer" : "Bien que notre service de support ne puisse pas vous aider directement pour ce type de souci, c'est un problème très courant que la plupart des plateformes d'échange ont l'habitude de gérer. Contactez simplement le support de la plateforme d'échange, expliquez que vous avez oublié d'inclure l'ID de paiement et envoyez leur l'ID de transaction comme preuve. Vous pouvez visualiser l'ID de transaction en tapant sur transaction sur l'écran de votre wallet.\n"
},
{
"question" : "Que signifient \"seed\" et \"clefs\" ?",
"answer" : "Vos clefs encodent l'information privée de votre wallet, ce sont elles qui permettent de dépenser vos fonds et de visualiser les transactions entrantes.\nVotre seed est simplement une version de votre clef privée sous une forme plus simple à recopier. Votre seed et vos clefs sont la même chose, juste sous des formes différentes !\nNE DONNEZ JAMAIS votre seed ou vos clefs à quiconque. Vos fonds seront dérobés si vous donner votre seed ou vos clefs. Merci d'écrire cependant votre seed et de le stocker en lieu sûr (afin de vous permettre de restaurer votre wallet si vous perdez votre téléphone.)\n"
},
{
"question" : "Combien de wallets puis-je créer ?",
"answer" : "Il n'y a pas de limite ! Vous pouvez créer autant de wallets que vous le souhaitez.\n"
},
{
"question" : "Commen puis-je restarurer mon wallet ?",
"answer" : "Appuyez sur le menu •••, sélectionnez Wallets, puis choisissez Restaurer un Wallet. Entrez alors votre seed (ou vos clefs), et de façon optionnelle une date antérieure à la première transaction de votre wallet (cela permettra d'accélérer le processus de synchronisation). Vous pourrez avoir besoin de maintenir l'application ouverte pendant 15 à 30 minutes afin de restaurer complètement votre wallet.\n"
},
{
"question" : "Que puis-je faire si je perds mon seed ?",
"answer" : "Si vous oubliez votre seed, il y a des chances que vous l'ayez inscrit quelque part. Vérifiez vos notes et regardez sur votre ordinateur. Si vous ne parvenez pas à le retrouver, il est possible que vous ayez effectué une sauvegarde de Cake Wallet (dans ce cas vous pourrez restaurer d'après cette sauvegarde). Si aucune des ces options ne convient, malheureusement il n'y a plus rien à faire, vos fonds sont définitivement perdus.\n"
},
{
"question" : "Collectez vous des informations à propos de mon wallet ?",
"answer" : "Cake Wallet NE COLLECTE PAS d'informations à propos de votre wallet. Nous sommes respectueux de votre intimité.\n"
},
{
"question" : "Est il possible d'annuler une transaction ?",
"answer" : "Malheureusement, dès qu'une transaction a été envoyée vers la blockchain, elle devient irréversible. Vous pouvez cependant annuler la transaction avant de l'envoyer, donc vérifiez toujours l'adresse avant d'envoyer une transaction.\n"
},
{
"question" : "Que sont les sous-adresses, et comment s'en servir ?",
"answer" : "Une sous-adresse est une adresse unique que vous pouvez générer à tout moment. Les montants envoyés vers cette adresse arrivent toujours dans votre wallet principal, mais la personne qui vous envoie les fonds ne peut pas déterminer votre adresse principale. Les sous-adresses commencent par un 8.\nVous pouvez créer une nouvelle sous-adresse dans l'écran Réception en appuyant sur le + à côté du bouton Sous-Adresses. Entrez un nom pour la sous-adresse et appuyez sur Ajouter. Ensuite appuyez sur le nom de la sous-adresse quand vous souhaitez l'utiliser !\nSi vous êtres paranoïaque, vous devriez créer une nouvelle sous-adresse à chaque fois que vous voulez recevoir des Monero.\n"
},
{
"question" : "Qu'est-ce que l'ID de transaction ?",
"answer" : "Une empreinte (hash) de transaction, ou ID de transaction, est un moyen unique d'identifier une transaction. Chaque transaction a sa propre empreinte. Si vous deve fournir une empreinte de transaction à quelque'un, allez simplement sur l'écran principal du Wallet, appuyez sur la transaction puis appuyez longuement sur la section du haut et sélectionnez Copier.\n"
},
{
"question" : "Je n'ai pas reçu mes XMR ! Que puis-je faire ?",
"answer" : "Si vous n'avez pas reçu vos Monero, appuyez sur le menu ••• et choisissez Reconnecter. Si cela ne règle pas le problème, allez dans le menu de réglage, appuyez sur le champ 'Nœud Courant' et sélectionnez un nœud avec un point vert.\n"
},
{
"question" : "Je n'ai pas reçu mes fonds en provenance de la plateforme d'échange dans l'application. Que puis-je faire ?",
"answer" : "Si vous avez des soucis avec une plateforme d'échange, le mieux est de contacter la plateforme d'échange directement. Nous avons des partenariats avec XMR.TO, Morph et ChangeNow, donc essayez http://xmr.to, http://changenow.io, ou http://morphtoken.com et contactez leur support.\n"
},
{
"question" : "Comment puis-je contacter le support de Cake Wallet ?",
"answer" : "Envoyez un email à support@cakewallet.com, rejoignez le groupe Telegram @cakewallet_bot, ou @CakeWalletXMR sur Twitter!\n"
}
]

BIN
assets/images/buy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/images/received.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
assets/images/sell.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -104,7 +104,7 @@ Risks Related to the use of the App
- Mistakes made by the user of any Monero-related and/or Bitcoin-related and/or Litecoin-related software or service, e.g., forgotten passwords, payments sent to wrong Monero and/or Bitcoin and/or Litecoin addresses, or accidental deletion of wallets;
- Software problems of the App and/or any Monero-related or Bitcoin-related or Litecoin-related oftware or service, e.g., corrupted wallet file, incorrectly constructed transactions, unsafe cryptographic libraries, malware affecting the App and/or any Monero-related or Bitcoin-related or Litecoin-related software or service;
- Software problems of the App and/or any Monero-related or Bitcoin-related or Litecoin-related software or service, e.g., corrupted wallet file, incorrectly constructed transactions, unsafe cryptographic libraries, malware affecting the App and/or any Monero-related or Bitcoin-related or Litecoin-related software or service;
- Technical failures in the hardware of the user of any Monero-related and/or Bitcoin-related and/or Litecoin-related software or service, e.g., data loss due to a faulty or damaged storage device;

View file

@ -41,7 +41,7 @@ CakeWallet cannot be built without the following packages installed on your buil
- openjdk-8-jre-headless
You may easily install them on your build sytem with the following command:
You may easily install them on your build system with the following command:
`$ sudo apt-get install -y unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake openjdk-8-jre-headless`

View file

@ -45,11 +45,11 @@
<key>NSCameraUsageDescription</key>
<string>Used for scan QR code</string>
<key>NSDocumentsFolderUsageDescription</key>
<string>We need access to documents folder for get acces to open/save backup file</string>
<string>We need access to documents folder for getting access to open/save backup file</string>
<key>NSFaceIDUsageDescription</key>
<string>Enable Face ID for fast and secure access to wallets and private keys</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to documents folder for get acces to open/save backup file</string>
<string>We need access to documents folder for getting access to open/save backup file</string>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>

View file

@ -4,9 +4,11 @@ class BuyAmount {
BuyAmount({
@required this.sourceAmount,
@required this.destAmount,
this.achSourceAmount,
this.minAmount = 0});
final double sourceAmount;
final double destAmount;
final double achSourceAmount;
final int minAmount;
}

View file

@ -107,8 +107,9 @@ class WyreBuyProvider extends BuyProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final sourceAmount = responseJSON['sourceAmount'] as double;
final destAmount = responseJSON['destAmount'] as double;
final achAmount = responseJSON['sourceAmountWithoutFees'] as double;
return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount);
return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount, achSourceAmount: achAmount);
}
@override

View file

@ -4,7 +4,7 @@ const bitcoinMnemonicLength = 12;
const moneroMnemonicLength = 25;
int mnemonicLength(WalletType type) {
// TODO: need to have only one place for get(set) mnemonic string lenth;
// TODO: need to have only one place for get(set) mnemonic string length;
switch (type) {
case WalletType.monero:

47
lib/core/yat_service.dart Normal file
View file

@ -0,0 +1,47 @@
import 'dart:convert';
import 'package:cake_wallet/entities/yat_record.dart';
import 'package:http/http.dart';
class YatService {
static bool isDevMode = false;
static String get apiUrl =>
YatService.isDevMode ? YatService.apiDevUrl : YatService.apiReleaseUrl;
static const apiReleaseUrl = "https://a.y.at";
static const apiDevUrl = 'https://yat.fyi';
static String lookupEmojiUrl(String emojiId) =>
"$apiUrl/emoji_id/$emojiId/payment";
static const tags = {
'XMR': '0x1001,0x1002',
'BTC': '0x1003',
'LTC': '0x3fff'
};
Future<List<YatRecord>> fetchYatAddress(String emojiId, String ticker) async {
final formattedTicker = ticker.toUpperCase();
final formattedEmojiId = emojiId.replaceAll(' ', '');
final uri = Uri.parse(lookupEmojiUrl(formattedEmojiId)).replace(
queryParameters: <String, dynamic>{
"tags": tags[formattedTicker]
});
final yatRecords = <YatRecord>[];
try {
final response = await get(uri);
final resBody = json.decode(response.body) as Map<String, dynamic>;
final results = resBody["result"] as Map<dynamic, dynamic>;
results.forEach((dynamic key, dynamic value) {
yatRecords.add(YatRecord.fromJson(value as Map<String, dynamic>));
});
return yatRecords;
} catch (_) {
return yatRecords;
}
}
}

View file

@ -1,8 +1,11 @@
import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/wake_lock.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cake_wallet/core/backup_service.dart';
import 'package:cw_core/wallet_service.dart';
@ -250,6 +253,7 @@ Future setup(
fiatConvertationStore: getIt.get<FiatConversionStore>()));
getIt.registerFactory(() => DashboardViewModel(
balanceViewModel: getIt.get<BalanceViewModel>(),
appStore: getIt.get<AppStore>(),
tradesStore: getIt.get<TradesStore>(),
@ -304,10 +308,10 @@ Future setup(
onAuthenticationFinished: onAuthFinished,
closable: closable ?? false));
getIt.registerFactory<DashboardPage>(() => DashboardPage(
walletViewModel: getIt.get<DashboardViewModel>(),
addressListViewModel: getIt.get<WalletAddressListViewModel>()));
getIt.registerFactory(() =>
BalancePage(dashboardViewModel: getIt.get<DashboardViewModel>(), settingsStore: getIt.get<SettingsStore>()));
getIt.registerFactory<DashboardPage>(() => DashboardPage( balancePage: getIt.get<BalancePage>(), walletViewModel: getIt.get<DashboardViewModel>(), addressListViewModel: getIt.get<WalletAddressListViewModel>()));
getIt.registerFactory<ReceivePage>(() => ReceivePage(
addressListViewModel: getIt.get<WalletAddressListViewModel>()));
@ -627,5 +631,9 @@ Future setup(
getIt.registerFactory(() => WakeLock());
getIt.registerFactory(() => YatService());
getIt.registerFactory(() => AddressResolver(yatService: getIt.get<YatService>()));
_isSetupFinished = true;
}

View file

@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
extension Emoji on String {
static final REGEX_EMOJI = RegExp(
r'[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}'
r'\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}'
r'-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}'
r'\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}'
r'-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}'
r'\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}'
r'-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}'
r'\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}'
r'-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}\u{200d}]+',
unicode: true,
);
bool _hasOnlyEmojis() {
final parsedText = this.replaceAll(' ', '');
for (final c in Characters(parsedText))
if (!REGEX_EMOJI.hasMatch(c)) return false;
return true;
}
/// Returns true if the given text contains only emojis.
bool get hasOnlyEmojis => _hasOnlyEmojis();
}

View file

@ -95,7 +95,7 @@ Future<void> ios_migrate_user_defaults() async {
}
await prefs.setBool('dark_theme', isDark);
//assign the pin lenght
//assign the pin length
final pinLength = await ios_legacy_helper.getInt('pin-length');
await prefs.setInt(PreferencesKey.currentPinLength, pinLength);

View file

@ -7,6 +7,7 @@ class LanguageService {
'en': 'English',
'de': 'Deutsch (German)',
'es': 'Español (Spanish)',
'fr': 'Français (French)',
'hi': 'हिंदी (Hindi)',
'ja': '日本 (Japanese)',
'ko': '한국어 (Korean)',

View file

@ -1,10 +1,17 @@
import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/entities/emoji_string_extension.dart';
import 'package:flutter/foundation.dart';
const unstoppableDomains = [
class AddressResolver {
AddressResolver({@required this.yatService});
final YatService yatService;
static const unstoppableDomains = [
'crypto',
'zil',
'x',
@ -17,57 +24,33 @@ const unstoppableDomains = [
'blockchain'
];
Future<ParsedAddress> parseAddressFromDomain(
String domain, String ticker) async {
try {
final formattedName = OpenaliasRecord.formatDomainName(domain);
final domainParts = formattedName.split('.');
final name = domainParts.last;
if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) {
try {
final addresses = await fetchYatAddress(domain, ticker);
if (addresses?.isEmpty ?? true) {
return ParsedAddress(
addresses: [domain], parseFrom: ParseFrom.yatRecord);
}
return ParsedAddress(
addresses: addresses, name: domain, parseFrom: ParseFrom.yatRecord);
} catch (e) {
return ParsedAddress(addresses: [domain]);
Future<ParsedAddress> resolve(String text, String ticker) async {
try {
if (text.hasOnlyEmojis) {
final addresses = await yatService.fetchYatAddress(text, ticker);
return ParsedAddress.fetchEmojiAddress(addresses: addresses, name: text);
}
}
final formattedName = OpenaliasRecord.formatDomainName(text);
final domainParts = formattedName.split('.');
final name = domainParts.last;
if (unstoppableDomains.any((domain) => name.contains(domain))) {
final address = await fetchUnstoppableDomainAddress(domain, ticker);
if (address?.isEmpty ?? true) {
return ParsedAddress(addresses: [domain]);
if (domainParts.length <= 1 || domainParts.first.isEmpty || name.isEmpty) {
return ParsedAddress(addresses: [text]);
}
return ParsedAddress(
addresses: [address],
name: domain,
parseFrom: ParseFrom.unstoppableDomains);
if (unstoppableDomains.any((domain) => name.contains(domain))) {
final address = await fetchUnstoppableDomainAddress(text, ticker);
return ParsedAddress.fetchUnstoppableDomainAddress(address: address, name: text);
}
final record = await OpenaliasRecord.fetchAddressAndName(
formattedName: formattedName, ticker: ticker);
return ParsedAddress.fetchOpenAliasAddress(record: record, name: text);
} catch (e) {
print(e.toString());
}
final record = await OpenaliasRecord.fetchAddressAndName(
formattedName: formattedName, ticker: ticker);
if (record == null || record.address.contains(formattedName)) {
return ParsedAddress(addresses: [domain]);
}
return ParsedAddress(
addresses: [record.address],
name: record.name,
description: record.description,
parseFrom: ParseFrom.openAlias);
} catch (e) {
print(e.toString());
return ParsedAddress(addresses: [text]);
}
return ParsedAddress(addresses: [domain]);
}

View file

@ -1,3 +1,7 @@
import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/yat_record.dart';
import 'package:flutter/material.dart';
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, notParsed }
class ParsedAddress {
@ -12,4 +16,46 @@ class ParsedAddress {
final String name;
final String description;
final ParseFrom parseFrom;
factory ParsedAddress.fetchEmojiAddress({
@required List<YatRecord> addresses,
@required String name,
}){
if (addresses?.isEmpty ?? true) {
return ParsedAddress(
addresses: [name], parseFrom: ParseFrom.yatRecord);
}
return ParsedAddress(
addresses: addresses.map((e) => e.address).toList(),
name: name,
parseFrom: ParseFrom.yatRecord,
);
}
factory ParsedAddress.fetchUnstoppableDomainAddress({
@required String address,
@required String name,
}){
if (address?.isEmpty ?? true) {
return ParsedAddress(addresses: [name]);
}
return ParsedAddress(
addresses: [address],
name: name,
parseFrom: ParseFrom.unstoppableDomains,
);
}
factory ParsedAddress.fetchOpenAliasAddress({@required OpenaliasRecord record, @required String name}){
final formattedName = OpenaliasRecord.formatDomainName(name);
if (record == null || record.address.contains(formattedName)) {
return ParsedAddress(addresses: [name]);
}
return ParsedAddress(
addresses: [record.address],
name: record.name,
description: record.description,
parseFrom: ParseFrom.openAlias,
);
}
}

View file

@ -0,0 +1,16 @@
class YatRecord {
String category;
String address;
YatRecord({
this.category,
this.address,
});
YatRecord.fromJson(Map<String, dynamic> json) {
address = json['address'] as String;
category = json['category'] as String;
}
}

View file

@ -2,9 +2,7 @@ import 'dart:async';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/language_service.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/src/screens/yat_emoji_id.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

View file

@ -177,12 +177,14 @@ class PreOrderPage extends BasePage {
builder: (context, AsyncSnapshot<BuyAmount> snapshot) {
double sourceAmount;
double destAmount;
double achAmount;
int minAmount;
if (snapshot.hasData) {
sourceAmount = snapshot.data.sourceAmount;
destAmount = snapshot.data.destAmount;
minAmount = snapshot.data.minAmount;
achAmount = snapshot.data.achSourceAmount;
} else {
sourceAmount = 0.0;
destAmount = 0.0;
@ -201,6 +203,7 @@ class PreOrderPage extends BasePage {
sourceCurrency: buyViewModel.fiatCurrency,
destAmount: destAmount,
destCurrency: buyViewModel.cryptoCurrency,
achSourceAmount: achAmount,
onTap: ((buyViewModel.doubleAmount != 0.0)
&& (snapshot.hasData)) ? () =>
onSelectBuyProvider(

View file

@ -3,6 +3,7 @@ import 'package:cake_wallet/buy/get_buy_provider_icon.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/palette.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class BuyListItem extends StatelessWidget {
@ -13,6 +14,7 @@ class BuyListItem extends StatelessWidget {
@required this.sourceCurrency,
@required this.destAmount,
@required this.destCurrency,
@required this.achSourceAmount,
@required this.onTap
});
@ -22,6 +24,7 @@ class BuyListItem extends StatelessWidget {
final FiatCurrency sourceCurrency;
final double destAmount;
final CryptoCurrency destCurrency;
final double achSourceAmount;
final void Function() onTap;
@override
@ -47,70 +50,100 @@ class BuyListItem extends StatelessWidget {
return GestureDetector(
onTap: () => onTap?.call(),
child: Container(
height: 102,
padding: EdgeInsets.only(
left: 20,
//top: 33,
top: 28,
bottom: 28,
right: 20
),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(25)),
color: backgroundColor
),
child: Stack(
child: Column(
children: [
Positioned(
top: 33,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (providerIcon != null) Padding(
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (providerIcon != null)
Padding(
padding: EdgeInsets.only(right: 10),
child: providerIcon
),
Text(
provider.description.title,
style: TextStyle(
color: secondaryTextColor,
fontSize: 20,
fontWeight: FontWeight.bold
),
)
],
),
Text(
'${destAmount?.toString()} ${destCurrency.title}',
style: TextStyle(
color: secondaryTextColor,
fontSize: 20,
fontWeight: FontWeight.bold
),
),
],
)
),
Positioned(
top: 65,
right: 0,
child: Text(
'${sourceAmount?.toString()} ${sourceCurrency.title}',
style: TextStyle(
color: primaryTextColor,
fontSize: 16,
fontWeight: FontWeight.bold
child: providerIcon),
Text(
provider.description.title,
style: TextStyle(
color: secondaryTextColor,
fontSize: 20,
fontWeight: FontWeight.bold),
)
],
),
),
)
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if(achSourceAmount != null)...[
Text(
'${destAmount?.toString()} ${destCurrency.title}',
style: TextStyle(
color: secondaryTextColor,
fontSize: 20,
fontWeight: FontWeight.bold),
),
Row(
children: [
Icon(
Icons.account_balance_outlined,
size: 12,
color: primaryTextColor,
),
SizedBox(width: 5),
Text(
'${achSourceAmount?.toString()} ${sourceCurrency.title}',
style: TextStyle(
color: primaryTextColor,
fontSize: 16,
fontWeight: FontWeight.bold),
),
],
),
SizedBox(height: 10),
],
Text(
'${destAmount?.toString()} ${destCurrency.title}',
style: TextStyle(
color: secondaryTextColor,
fontSize: 20,
fontWeight: FontWeight.bold),
),
Row(
children: [
Icon(
CupertinoIcons.creditcard,
size: 12,
color: primaryTextColor,
),
SizedBox(width: 5),
Text(
'${sourceAmount?.toString()} ${sourceCurrency.title}',
style: TextStyle(
color: primaryTextColor,
fontSize: 16,
fontWeight: FontWeight.bold),
),
],
),
],
),
],
),
],
)
),
)
);
}

View file

@ -30,9 +30,11 @@ import 'package:cake_wallet/wallet_type_utils.dart';
class DashboardPage extends BasePage {
DashboardPage({
@required this.balancePage,
@required this.walletViewModel,
@required this.addressListViewModel,
});
final BalancePage balancePage;
@override
Color get backgroundLightColor =>
@ -60,7 +62,9 @@ class DashboardPage extends BasePage {
@override
Widget middle(BuildContext context) {
return SyncIndicator(dashboardViewModel: walletViewModel);
return SyncIndicator(dashboardViewModel: walletViewModel,
onTap: () => Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.nodeList));
}
@override
@ -90,19 +94,23 @@ class DashboardPage extends BasePage {
@override
Widget body(BuildContext context) {
final sendImage = Image.asset('assets/images/upload.png',
height: 22.24,
height: 24,
width: 24,
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
final receiveImage = Image.asset('assets/images/received.png',
height: 24,
width: 24,
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
final exchangeImage = Image.asset('assets/images/transfer.png',
height: 24.27,
width: 22.25,
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
final buyImage = Image.asset('assets/images/coins.png',
height: 22.24,
height: 24,
width: 24,
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
final sellImage = Image.asset('assets/images/restore_wallet_image.png',
height: 22.24,
final buyImage = Image.asset('assets/images/buy.png',
height: 24,
width: 24,
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
final sellImage = Image.asset('assets/images/sell.png',
height: 24,
width: 24,
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
_setEffects(context);
@ -132,35 +140,50 @@ class DashboardPage extends BasePage {
.display1
.backgroundColor),
)),
Container(
padding: EdgeInsets.only(left: 45, right: 45, bottom: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
ActionButton(
image: sendImage,
title: S.of(context).send,
route: Routes.send),
if (!isHaven)
ActionButton(
image: exchangeImage,
title: S.of(context).exchange,
route: Routes.exchange),
if (!isMoneroOnly && !isHaven)
ActionButton(
image: buyImage,
title: S.of(context).buy,
onClick: () async => await _onClickBuyButton(context),
ClipRect(
child:Container(
margin: const EdgeInsets.only(left: 16, right: 16, bottom: 38),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50.0),
border: Border.all(color: currentTheme.type == ThemeType.bright ? Color.fromRGBO(255, 255, 255, 0.2): Colors.transparent, width: 1, ),
color:Theme.of(context).textTheme.title.backgroundColor
),
if (!isMoneroOnly && !isHaven)
child: Container(
padding: EdgeInsets.only(left: 32, right: 32, bottom: 14, top: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
if (!isMoneroOnly && !isHaven)
ActionButton(
image: buyImage,
title: S.of(context).buy,
onClick: () async => await _onClickBuyButton(context),
),
ActionButton(
image: sellImage,
title: S.of(context).sell,
onClick: () async => await _onClickSellButton(context),
),
],
),
)
image: receiveImage,
title: S.of(context).receive,
route: Routes.receive),
if (!isHaven)
ActionButton(
image: exchangeImage,
title: S.of(context).exchange,
route: Routes.exchange),
ActionButton(
image: sendImage,
title: S.of(context).send,
route: Routes.send),
if (!isMoneroOnly && !isHaven)
ActionButton(
image: sellImage,
title: S.of(context).sell,
onClick: () async => await _onClickSellButton(context),
),
],
),),
),),),
],
));
}
@ -173,28 +196,10 @@ class DashboardPage extends BasePage {
pages.add(AddressPage(
addressListViewModel: addressListViewModel,
walletViewModel: walletViewModel));
pages.add(BalancePage(dashboardViewModel: walletViewModel));
pages.add(balancePage);
pages.add(TransactionsPage(dashboardViewModel: walletViewModel));
_isEffectsInstalled = true;
//if (walletViewModel.shouldShowYatPopup) {
// await Future<void>.delayed(Duration(seconds: 1));
// if (currentRouteSettings.name == Routes.preSeed
// || currentRouteSettings.name == Routes.seed) {
// return;
// }
// await showPopUp<void>(
// context: context,
// builder: (BuildContext context) {
// return YatPopup(
// dashboardViewModel: walletViewModel,
// onClose: () => Navigator.of(context).pop());
// });
// walletViewModel.furtherShowYatPopup(false);
//}
autorun((_) async {
if (!walletViewModel.isOutdatedElectrumWallet) {
return;

View file

@ -20,7 +20,6 @@ class ActionButton extends StatelessWidget {
alignment: alignment,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
GestureDetector(
@ -32,19 +31,17 @@ class ActionButton extends StatelessWidget {
}
},
child: Container(
height: 60,
width: 60,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).buttonColor, shape: BoxShape.circle),
shape: BoxShape.circle),
child: image,
),
),
SizedBox(height: 15),
SizedBox(height: 4),
Text(
title,
style: TextStyle(
fontSize: 14,
fontSize: 10,
color: Theme.of(context).accentTextTheme.display3
.backgroundColor),
)

View file

@ -1,15 +1,20 @@
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:auto_size_text/auto_size_text.dart';
class BalancePage extends StatelessWidget {
BalancePage({@required this.dashboardViewModel});
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(
@ -19,161 +24,182 @@ class BalancePage extends StatelessWidget {
onLongPressUp: () =>
dashboardViewModel.balanceViewModel.isReversing =
!dashboardViewModel.balanceViewModel.isReversing,
child: Container(
color: Colors.transparent,
padding: EdgeInsets.all(24),
child: Observer(builder: (BuildContext context) {
if (dashboardViewModel.balanceViewModel.hasMultiBalance) {
return ListView.separated(
shrinkWrap: true,
separatorBuilder: (_, __) => StandardListSeparator(padding: EdgeInsets.only(left: 24)),
itemCount: dashboardViewModel.balanceViewModel.formattedBalances.length,
itemBuilder: (__, index) {
final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index);
final cur = balance.asset.toString();
child: Column(
children: [
SizedBox(height: 56),
Container(
alignment: Alignment.topLeft,
margin: const EdgeInsets.only(left: 24, bottom: 16),
child: Observer(builder: (_) {
return AutoSizeText(
dashboardViewModel.balanceViewModel.asset,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
height: 1),
maxLines: 1,
textAlign: TextAlign.center);
})),
return Container(
padding: EdgeInsets.only(left: 5, right: 15, bottom: 15, top: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
decoration: BoxDecoration(
color: Theme.of(context).buttonColor,
border: Border.all(color: Theme.of(context).buttonColor),
borderRadius: BorderRadius.all(Radius.circular(20))
),
padding: EdgeInsets.only(left: 12, right: 12, top: 7, bottom: 7),
child: Text(
'${cur.toString()}',
style: TextStyle(
fontSize: 18,
color: Theme.of(context).accentTextTheme.display3.backgroundColor))),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
'${balance.additionalBalance}',
style: TextStyle(
fontSize: 22,
color: Theme.of(context).accentTextTheme.display3.backgroundColor)),
Text(
'Full balance ${balance.fiatAdditionalBalance}',
style: TextStyle(
fontSize: 12,
color: Theme.of(context).accentTextTheme.display2.backgroundColor)),
Container(height: 5),
Text(
'${balance.availableBalance}',
style: TextStyle(
fontSize: 22,
color: Theme.of(context).accentTextTheme.display3.backgroundColor)),
Text(
'Available balance ${balance.fiatAvailableBalance}',
style: TextStyle(
fontSize: 12,
color: Theme.of(context).accentTextTheme.display2.backgroundColor)),
],)
]));
});
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Observer(builder: (_) {
return Text(
dashboardViewModel.balanceViewModel.currency.toString(),
style: TextStyle(
fontSize: 40,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.accentTextTheme
.display2
.backgroundColor,
height: 1),
);
}),
SizedBox(height: 10),
Observer(builder: (_) {
return Row(
children: [
Expanded(
child: Text(
'${dashboardViewModel.balanceViewModel.availableBalanceLabel} (${dashboardViewModel.balanceViewModel.availableFiatBalance.toString()})',
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.title.backgroundColor
),
child: Container(
margin: const EdgeInsets.only(top: 24, left: 24, right: 24, bottom: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 8,),
Observer(builder: (_) {
return Column(
children: [
Text(
'${dashboardViewModel.balanceViewModel.availableBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme
.display2
.backgroundColor,
height: 1),
)
)
],
);
}),
SizedBox(height: 10),
Observer(builder: (_) {
return AutoSizeText(
dashboardViewModel.balanceViewModel.availableBalance,
style: TextStyle(
fontSize: 54,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
height: 1),
maxLines: 1,
textAlign: TextAlign.center);
}),
SizedBox(height: 10),
Observer(builder: (_) {
return Row(
children: [
Expanded(
child: Text(
'${dashboardViewModel.balanceViewModel.additionalBalanceLabel} (${dashboardViewModel.balanceViewModel.additionalFiatBalance.toString()})',
],
);
}),
SizedBox(height: 8,),
Observer(builder: (_) {
return AutoSizeText(
dashboardViewModel.balanceViewModel.availableBalance,
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
height: 1),
maxLines: 1,
textAlign: TextAlign.center);
}),
SizedBox(height: 4,),
Observer(builder: (_) {
return Column(
children: [
Text(
'${dashboardViewModel.balanceViewModel.availableFiatBalance.toString()}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
height: 1),
)
],
);
}),
SizedBox(height: 26),
Observer(builder: (_) {
return Column(
children: [
Text(
'${dashboardViewModel.balanceViewModel.additionalBalanceLabel}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme
.display2
.backgroundColor,
height: 1),
)
)
],
);
}),
SizedBox(height: 10),
Observer(builder: (_) {
return AutoSizeText(
dashboardViewModel.balanceViewModel.additionalBalance
.toString(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
height: 1),
maxLines: 1,
textAlign: TextAlign.center);
}),
],
);
],
);
}),
SizedBox(height: 8),
Observer(builder: (_) {
return AutoSizeText(
dashboardViewModel.balanceViewModel.additionalBalance
.toString(),
style: TextStyle(
fontSize: 24,
fontFamily: 'Lato',
fontWeight: FontWeight.w900,
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
height: 1),
maxLines: 1,
textAlign: TextAlign.center);
}),
SizedBox(height: 4,),
Observer(builder: (_) {
return Column(
children: [
Text(
'${dashboardViewModel.balanceViewModel.additionalFiatBalance.toString()}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
height: 1),
)
],
);
}),
],
),
Observer(builder: (_) {
return Text(
dashboardViewModel.balanceViewModel.currency.toString(),
style: TextStyle(
fontSize: 28,
fontFamily: 'Lato',
fontWeight: FontWeight.w800,
color: Theme.of(context)
.accentTextTheme
.display3
.backgroundColor,
height: 1),
);
}),
],
),
),
),
}),
],
),
);
}
}

View file

@ -1,14 +1,15 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/core/sync_status_title.dart';
import 'package:cake_wallet/palette.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
class SyncIndicator extends StatelessWidget {
SyncIndicator({@required this.dashboardViewModel});
SyncIndicator({@required this.dashboardViewModel,this.onTap});
final DashboardViewModel dashboardViewModel;
final Function() onTap;
@override
Widget build(BuildContext context) {
@ -22,12 +23,12 @@ class SyncIndicator extends StatelessWidget {
final indicatorWidth = progress < 1
? indicatorOffset > 0 ? indicatorOffset : 0.0
: syncIndicatorWidth;
final indicatorColor = status is SyncedSyncStatus
? PaletteDark.brightGreen
: Theme.of(context).textTheme.caption.color;
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(15)),
child: GestureDetector(
onTap: onTap,
child: Container(
height: 30,
width: syncIndicatorWidth,
@ -57,14 +58,7 @@ class SyncIndicator extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 4,
width: 4,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: indicatorColor
),
),
SyncIndicatorIcon(isSynced:status is SyncedSyncStatus),
Padding(
padding: EdgeInsets.only(left: 6),
child: Text(
@ -82,7 +76,7 @@ class SyncIndicator extends StatelessWidget {
],
),
),
);
));
}
);
}

View file

@ -0,0 +1,21 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
class SyncIndicatorIcon extends StatelessWidget {
SyncIndicatorIcon({this.isSynced});
final bool isSynced;
@override
Widget build(BuildContext context) {
return Container(
height: 4,
width: 4,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isSynced
? PaletteDark.brightGreen
: Theme.of(context).textTheme.caption.color),
);
}
}

View file

@ -1,5 +1,5 @@
import 'dart:ui';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/utils/debounce.dart';
import 'package:cw_core/sync_status.dart';
import 'package:cw_core/wallet_type.dart';
@ -32,6 +32,7 @@ import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/amount_validator.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
class ExchangePage extends BasePage {
ExchangePage(this.exchangeViewModel);
@ -65,8 +66,17 @@ class ExchangePage extends BasePage {
AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override
Widget middle(BuildContext context) =>
PresentProviderPicker(exchangeViewModel: exchangeViewModel);
Widget middle(BuildContext context) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(right:6.0),
child: Observer(builder: (_) => SyncIndicatorIcon(isSynced: exchangeViewModel.status is SyncedSyncStatus),)
),
PresentProviderPicker(exchangeViewModel: exchangeViewModel)
],
);
@override
Widget trailing(BuildContext context) => TrailButton(
@ -780,7 +790,7 @@ class ExchangePage extends BasePage {
Future<String> fetchParsedAddress(
BuildContext context, String domain, String ticker) async {
final parsedAddress = await parseAddressFromDomain(domain, ticker);
final parsedAddress = await getIt.get<AddressResolver>().resolve(domain, ticker);
final address = await extractAddressFromParsed(context, parsedAddress);
return address;
}

View file

@ -82,6 +82,7 @@ class MoneroAccountListPage extends StatelessWidget {
alignment: Alignment.center,
children: <Widget>[
ListView.separated(
padding: EdgeInsets.zero,
controller: controller,
separatorBuilder: (context, index) =>
Container(

View file

@ -44,7 +44,7 @@ class AccountTile extends StatelessWidget {
),
);
return isCurrent ? cell : Slidable(
return Slidable(
key: Key(accountName),
child: cell,
actionPane: SlidableDrawerActionPane(),

View file

@ -39,7 +39,18 @@ class AddressCell extends StatelessWidget {
final Function(String) onTap;
final Function() onEdit;
String get label => name ?? address;
String get label {
if (name.isEmpty){
if(address.length<=16){
return address;
}else{
return address.substring(0,8)+'...'+
address.substring(address.length-8,address.length);
}
}else{
return name;
}
}
@override
Widget build(BuildContext context) {
@ -49,7 +60,7 @@ class AddressCell extends StatelessWidget {
color: backgroundColor,
padding: EdgeInsets.only(left: 24, right: 24, top: 28, bottom: 28),
child: Text(
name ?? address,
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
@ -59,9 +70,9 @@ class AddressCell extends StatelessWidget {
),
));
return (isCurrent || isPrimary)
? cell
: Slidable(
return Container(
color: backgroundColor,
child: Slidable(
key: Key(address),
actionPane: SlidableDrawerActionPane(),
child: cell,
@ -71,6 +82,6 @@ class AddressCell extends StatelessWidget {
color: Colors.blue,
icon: Icons.edit,
onTap: () => onEdit?.call())
]);
]));
}
}

View file

@ -1,6 +1,6 @@
import 'dart:ui';
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
import 'package:cake_wallet/src/screens/yat/widgets/yat_close_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
@ -23,8 +23,6 @@ import 'package:dotted_border/dotted_border.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/src/screens/yat/yat_sending.dart';
import 'package:cw_core/crypto_currency.dart';
class SendPage extends BasePage {
@ -51,6 +49,17 @@ class SendPage extends BasePage {
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override
Widget middle(BuildContext context) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(right:8.0),
child: Observer(builder: (_) => SyncIndicatorIcon(isSynced: sendViewModel.isReadyForSend),),
),super.middle(context),
],
);
@override
Widget trailing(context) => Observer(builder: (_) {
return sendViewModel.isBatchSending
@ -310,11 +319,6 @@ class SendPage extends BasePage {
await sendViewModel.createTransaction();
if (!sendViewModel.isBatchSending &&
sendViewModel.hasYat) {
await Navigator.of(context)
.push<void>(YatSending.createRoute(sendViewModel));
}
},
text: S.of(context).send,
color: Theme.of(context).accentTextTheme.body2.color,
@ -351,7 +355,7 @@ class SendPage extends BasePage {
}
if (state is ExecutedSuccessfullyState &&
!(!sendViewModel.isBatchSending && sendViewModel.hasYat)) {
!sendViewModel.isBatchSending) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,

View file

@ -1,15 +0,0 @@
import 'package:flutter/material.dart';
class CircleClipper extends CustomClipper<Path> {
CircleClipper(this.center, this.radius);
final Offset center;
final double radius;
@override
Path getClip(Size size) =>
Path()..addOval(Rect.fromCircle(radius: radius, center: center));
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => true;
}

View file

@ -1,124 +0,0 @@
import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:lottie/lottie.dart';
class YatAlert extends StatelessWidget {
YatAlert(this.yatStore);
final YatStore yatStore;
static const aspectRatioImage = 1.133;
final animation = Lottie.asset('assets/animation/anim1.json');
@override
Widget build(BuildContext context) {
final screenHeight = MediaQuery.of(context).size.height;
final screenWidth = MediaQuery.of(context).size.width;
return Container(
height: screenHeight,
width: screenWidth,
color: Colors.white,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(top: 40, bottom: 40),
content: Column(
children: [
Container(
height: 45,
padding: EdgeInsets.only(left: 24, right: 24),
child: YatBar(onClose: () => Navigator.of(context).pop())
),
animation,
Container(
padding: EdgeInsets.only(left: 30, right: 30),
child: Column(
children: [
Text(
S.of(context).yat_alert_title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
fontFamily: 'Lato',
color: Colors.black,
decoration: TextDecoration.none,
)
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Text(
S.of(context).yat_alert_content,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
fontFamily: 'Lato',
color: Colors.black,
decoration: TextDecoration.none,
)
)
)
]
)
)
]
),
bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 40),
bottomSection: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
PrimaryIconButton(
text: S.of(context).get_your_yat,
textColor: Colors.white,
color: Palette.protectiveBlue,
borderColor: Palette.protectiveBlue,
iconColor: Colors.white,
iconBackgroundColor: Colors.transparent,
iconData: CupertinoIcons
.arrow_up_right_square,
mainAxisAlignment: MainAxisAlignment.end,
onPressed: () {
var createNewYatUrl = YatLink.startFlowUrl;
final createNewYatUrlParameters =
yatStore.defineQueryParameters();
if (createNewYatUrlParameters.isNotEmpty) {
createNewYatUrl += '?sub1=' + createNewYatUrlParameters;
}
launch(createNewYatUrl, forceSafariVC: false);
}),
Padding(
padding: EdgeInsets.only(top: 24),
child: PrimaryIconButton(
text: S.of(context).connect_an_existing_yat,
textColor: Colors.black,
color: Palette.blueAlice,
borderColor: Palette.blueAlice,
iconColor: Colors.black,
iconBackgroundColor: Colors.transparent,
iconData: CupertinoIcons
.arrow_up_right_square,
mainAxisAlignment: MainAxisAlignment.end,
onPressed: () {
String url = YatLink.baseUrl + YatLink.signInSuffix;
final parameters =
yatStore.defineQueryParameters();
if (parameters.isNotEmpty) {
url += YatLink.queryParameter + parameters;
}
launch(url, forceSafariVC: false);
})
)
]
),
)
);
}
}

View file

@ -1,141 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/src/screens/yat/widgets/yat_close_button.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:cake_wallet/src/screens/yat/circle_clipper.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
class YatSending extends BasePage {
YatSending(this.sendViewModel);
static Route createRoute(SendViewModel sendViewModel) {
return PageRouteBuilder<void>(
transitionDuration: Duration(seconds: 1),
reverseTransitionDuration: Duration(seconds: 1),
opaque: false,
barrierDismissible: false,
pageBuilder: (context, animation, secondaryAnimation) => YatSending(sendViewModel),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final screenSize = MediaQuery.of(context).size;
final center = Offset(screenSize.width / 2, screenSize.height / 2);
final endRadius = screenSize.height * 1.2;
final tween = Tween(begin: 0.0, end: endRadius);
return ClipPath(
clipper: CircleClipper(center, animation.drive(tween).value),
child: child,
);
},
);
}
final SendViewModel sendViewModel;
@override
Color get titleColor => Colors.white;
@override
bool get resizeToAvoidBottomInset => false;
@override
bool get extendBodyBehindAppBar => true;
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override
Widget trailing(context) =>
YatCloseButton(onClose: () => Navigator.of(context).pop());
@override
Widget leading(BuildContext context) => Container();
@override
Widget body(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
return Container(
color: Colors.black,
child: Stack(
children: [
//Center(
// child:FutureBuilder<String>(
// future: visualisationForEmojiId(sendViewModel.outputs.first.address),
// builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
// switch (snapshot.connectionState) {
// case ConnectionState.done:
// if (snapshot.hasError || snapshot.data.isEmpty) {
// return Image.asset('assets/images/yat_logo.png', width: screenWidth, color: Colors.white);
// }
// return Image.network(
// snapshot.data,
// scale: 0.7,
// loadingBuilder: (Object z, Widget child, ImageChunkEvent loading)
// => loading != null
// ? CupertinoActivityIndicator(animating: true)
// : child);
// default:
// return Image.asset('assets/images/yat_logo.png', width: screenWidth, color: Colors.white);
// }
// }),
// ),
Positioned(
bottom: 20,
child: Container(
width: screenWidth,
padding: EdgeInsets.fromLTRB(20, 0, 20, 10),
child: Column(children: [
Text(
'You are sending ${sendViewModel.outputs.first.cryptoAmount} ${sendViewModel.currency.title} to ${sendViewModel.outputs.first.address}'.toUpperCase(),
style: TextStyle(
fontSize: 28,
decoration: TextDecoration.none,
color: Theme.of(context).accentTextTheme.display3.backgroundColor),
textAlign: TextAlign.center),
Container(height: 30),
LoadingPrimaryButton(
onPressed: () {
sendViewModel.commitTransaction();
showPopUp<void>(
context: context,
builder: (BuildContext popContext) {
return Observer(builder: (_) {
final state = sendViewModel.state;
if (state is FailureState) {
Navigator.of(context).pop();
}
if (state is TransactionCommitted) {
return AlertWithOneAction(
alertTitle: '',
alertContent: S.of(popContext).send_success(
sendViewModel.currency
.toString()),
buttonText: S.of(popContext).ok,
buttonAction: () {
Navigator.of(popContext).pop();
Navigator.of(context).pop();
});
}
return Offstage();
});
});
},
text: S.of(context).confirm_sending,
color: Theme.of(context).accentTextTheme.body2.color,
textColor: Colors.white,
isLoading: sendViewModel.state is IsExecutingState ||
sendViewModel.state is TransactionCommitting)])))]));
}
}

View file

@ -2,6 +2,7 @@ class LanguageList {
static const english = 'English';
static const chineseSimplified = 'Chinese (simplified)';
static const dutch = 'Dutch';
static const french = 'French';
static const german = 'German';
static const japanese = 'Japanese';
static const portuguese = 'Portuguese';

View file

@ -103,7 +103,7 @@ abstract class BuyViewModelBase with Store {
print(e.toString());
}
if (isMoonPayEnabled) {
if (isMoonPayEnabled ?? false) {
_providerList.add(MoonPayBuyProvider(wallet: wallet));
}

View file

@ -59,6 +59,22 @@ abstract class BalanceViewModelBase with Store {
@computed
BalanceDisplayMode get savedDisplayMode => settingsStore.balanceDisplayMode;
@computed
String get asset {
switch(appStore.wallet.currency){
case CryptoCurrency.btc:
return 'Bitcoin Assets';
case CryptoCurrency.xmr:
return 'Monero Assets';
case CryptoCurrency.ltc:
return 'Litecoin Assets';
default:
return '';
}
}
@computed
BalanceDisplayMode get displayMode => isReversing
? savedDisplayMode == BalanceDisplayMode.hiddenBalance
@ -118,11 +134,10 @@ abstract class BalanceViewModelBase with Store {
return '---';
}
return fiatCurrency.toString() +
' ' +
_getFiatBalance(
return _getFiatBalance(
price: price,
cryptoAmount: walletBalance.formattedAvailableBalance);
cryptoAmount: walletBalance.formattedAvailableBalance) + ' ' + fiatCurrency.toString();
}
@computed
@ -134,11 +149,10 @@ abstract class BalanceViewModelBase with Store {
return '---';
}
return fiatCurrency.toString() +
' ' +
_getFiatBalance(
return _getFiatBalance(
price: price,
cryptoAmount: walletBalance.formattedAdditionalBalance);
cryptoAmount: walletBalance.formattedAdditionalBalance) + ' ' + fiatCurrency.toString();
}
@computed
@ -230,3 +244,4 @@ abstract class BalanceViewModelBase with Store {
return calculateFiatAmount(price: price, cryptoAmount: cryptoAmount);
}
}

View file

@ -1,3 +1,4 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/parsed_address.dart';
@ -229,7 +230,7 @@ abstract class OutputBase with Store {
Future<void> fetchParsedAddress(BuildContext context) async {
final domain = address;
final ticker = cryptoCurrencyHandler().title.toLowerCase();
parsedAddress = await parseAddressFromDomain(domain, ticker);
parsedAddress = await getIt.get<AddressResolver>().resolve(domain, ticker);
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
note = parsedAddress.description;
}

View file

@ -1,4 +1,3 @@
import 'package:cake_wallet/src/screens/yat/yat_alert.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart';

528
res/values/strings_fr.arb Normal file
View file

@ -0,0 +1,528 @@
{
"welcome" : "Bienvenue sur",
"cake_wallet" : "Cake Wallet",
"first_wallet_text" : "Super wallet pour Monero, Bitcoin et Litecoin",
"please_make_selection" : "Merci de faire un choix ci-dessous pour créer ou restaurer votre wallet.",
"create_new" : "Créer un Nouveau Wallet",
"restore_wallet" : "Restaurer un Wallet",
"monero_com": "Monero.com par Cake Wallet",
"monero_com_wallet_text": "Super wallet pour Monero",
"accounts" : "Comptes",
"edit" : "Modifier",
"account" : "Compte",
"add" : "Ajouter",
"address_book" : "Carnet d'Adresses",
"contact" : "Contact",
"please_select" : "Merci de sélectionner :",
"cancel" : "Annuler",
"ok" : "OK",
"contact_name" : "Nom de Contact",
"reset" : "Remise à zéro",
"save" : "Sauvegarder",
"address_remove_contact" : "Supprimer contact",
"address_remove_content" : "Êtes vous certain de vouloir supprimer le contact sélectionné ?",
"authenticated" : "Authentifié",
"authentication" : "Authentification",
"failed_authentication" : "Écher d'authentication. ${state_error}",
"wallet_menu" : "Menu",
"Blocks_remaining" : "Blocs Restants : ${status}",
"please_try_to_connect_to_another_node" : "Merci d'essayer la connexion vers une autre node",
"xmr_hidden" : "Caché",
"xmr_available_balance" : "Solde Disponible",
"xmr_full_balance" : "Solde Total",
"send" : "Envoyer",
"receive" : "Recevoir",
"transactions" : "Transactions",
"incoming" : "Entrantes",
"outgoing" : "Sortantes",
"transactions_by_date" : "Transactions par date",
"trades" : "Échanges",
"filters" : "Filtre",
"today" : "Aujourd'hui",
"yesterday" : "Hier",
"received" : "Reçus",
"sent" : "Envoyés",
"pending" : " (en attente)",
"rescan" : "Rescan",
"reconnect" : "Reconnecter",
"wallets" : "Wallets",
"show_seed" : "Visualiser le seed",
"show_keys" : "Visualiser seed/clefs",
"address_book_menu" : "Carnet d'Adresses",
"reconnection" : "Reconnexion",
"reconnect_alert_text" : "Êtes vous certain de vouloir vous reconnecter ?",
"exchange" : "Échange",
"clear" : "Effacer",
"refund_address" : "Adresse de Remboursement",
"change_exchange_provider" : "Changer de Plateforme d'Échange",
"you_will_send" : "Convertir depuis",
"you_will_get" : "Convert vers",
"amount_is_guaranteed" : "Le montant reçu est garanti",
"amount_is_estimate" : "Le montant reçu est estimé",
"powered_by" : "Proposé par ${title}",
"error" : "Erreur",
"estimated" : "Estimé",
"min_value" : "Min: ${value} ${currency}",
"max_value" : "Max: ${value} ${currency}",
"change_currency" : "Change de Monnaie",
"copy_id" : "Copier l'ID",
"exchange_result_write_down_trade_id" : "Merci de copier ou d'écrire l'ID d'échange pour continuer.",
"trade_id" : "ID d'échange :",
"copied_to_clipboard" : "Copié vers le Presse-Papier",
"saved_the_trade_id" : "J'ai sauvé l'ID d'échange",
"fetching" : "Récupération",
"id" : "ID : ",
"amount" : "Montant : ",
"payment_id" : "ID de Paiement : ",
"status" : "Statut : ",
"offer_expires_in" : "L'Offre expire dans : ",
"trade_is_powered_by" : "Cet échange est proposé par ${provider}",
"copy_address" : "Copier l'Adresse",
"exchange_result_confirm" : "En pressant confirmer, vous enverrez ${fetchingLabel} ${from} depuis votre wallet nommé ${walletName} vers l'adresse ci-dessous. Vous pouvez aussi envoyer depuis votre wallet externe vers l'adresse/QR code ci-dessous.\n\nMerci de presser confirmer pour continuer ou retournez en arrière pour modifier les montants.",
"exchange_result_description" : "Vous devez envoyer un minimum de ${fetchingLabel} ${from} à l'adresse indiquée page suivante. Si vous envoyez un montant inférieur à ${fetchingLabel} ${from} il pourrait ne pas être converti et ne pas être remboursé.",
"exchange_result_write_down_ID" : "*Merci de copier ou écrire votre ID présenté ci-dessus.",
"confirm" : "Confirmer",
"confirm_sending" : "Confirmer l'envoi",
"commit_transaction_amount_fee" : "Valider la transaction\nMontant : ${amount}\nFrais : ${fee}",
"sending" : "Envoi",
"transaction_sent" : "Transaction émise !",
"expired" : "Expirée",
"time" : "${minutes}m ${seconds}s",
"send_xmr" : "Envoyer XMR",
"exchange_new_template" : "Nouveau gabarit",
"faq" : "FAQ",
"enter_your_pin" : "Entrez votre code PIN",
"loading_your_wallet" : "Chargement de votre wallet",
"new_wallet" : "Nouveau Wallet",
"wallet_name" : "Nom du Wallet",
"continue_text" : "Continuer",
"choose_wallet_currency" : "Merci de choisir la monnaie du wallet :",
"node_new" : "Nouveau Node",
"node_address" : "Adresse du Nœud",
"node_port" : "Port du Nœud",
"login" : "Utilisateur",
"password" : "Mot de passe",
"nodes" : "Nœuds",
"node_reset_settings_title" : "Réinitialisation des réglages",
"nodes_list_reset_to_default_message" : "Êtes vous certain de vouloir revenir aux réglages par défaut ?",
"change_current_node" : "Êtes vous certain de vouloir changer le nœud courant vers ${node}?",
"change" : "Changer",
"remove_node" : "Supprimer le nœud",
"remove_node_message" : "Êtes vous certain de vouloir supprimer le nœud sélectionné ?",
"remove" : "Supprimer",
"delete" : "Effacer",
"add_new_node" : "Ajouter un nouveau nœud",
"change_current_node_title" : "Changer le nœud courant",
"node_test" : "Tester",
"node_connection_successful" : "Connexion établie avec succès",
"node_connection_failed" : "La connexion a échoué",
"new_node_testing" : "Test du nouveau nœud",
"use" : "Changer ver ",
"digit_pin" : "-digit PIN",
"share_address" : "Partager l'adresse",
"receive_amount" : "Montant",
"subaddresses" : "Sous-adresses",
"addresses" : "Adresses",
"scan_qr_code" : "Scannez le QR code pour obtenir l'adresse",
"rename" : "Renommer",
"choose_account" : "Choisir le compte",
"create_new_account" : "Créer un nouveau compte",
"accounts_subaddresses" : "Comptes et sous-adresses",
"restore_restore_wallet" : "Restaurer le Wallet",
"restore_title_from_seed_keys" : "Restaurer depuis un seed ou des clefs",
"restore_description_from_seed_keys" : "Restaurez votre wallet depuis un seed ou des clefs que vous avez stockés en lieu sûr",
"restore_next" : "Suivant",
"restore_title_from_backup" : "Restaured depuis une sauvegarde",
"restore_description_from_backup" : "Vous pouvez restaurer l'intégralité de l'application Cake Wallet depuis un fichier de sauvegarde",
"restore_seed_keys_restore" : "Restaurer depuis Seed/Clefs",
"restore_title_from_seed" : "Restaurer depuis seed",
"restore_description_from_seed" : "Restaurez votre wallet depuis une combinaison de 25 ou 13 mots",
"restore_title_from_keys" : "Restaurer depuis des clefs",
"restore_description_from_keys" : "Restaurer votre wallet d'après les séquences de touches générées d'après vos clefs privées",
"restore_wallet_name" : "Nom du wallet",
"restore_address" : "Adresse",
"restore_view_key_private" : "Clef d'audit (view key) (privée)",
"restore_spend_key_private" : "Clef de dépense (spend key) (privée)",
"restore_recover" : "Restaurer",
"restore_wallet_restore_description" : "Description de la restauration de wallet",
"restore_new_seed" : "Nouveau seed",
"restore_active_seed" : "Seed actif",
"restore_bitcoin_description_from_seed" : "Restaurer votre wallet à l'aide d'une combinaison de 24 mots",
"restore_bitcoin_description_from_keys" : "Restaurer votre wallet d'après la chaîne WIF générée d'après vos clefs privées",
"restore_bitcoin_title_from_keys" : "Restaurer depuis la chaîne WIF",
"restore_from_date_or_blockheight" : "Merci d'entrer une date antérieure de quelques jours à la date de création de votre wallet. Si vous connaissez la hauteur de bloc, merci de la spécifier plutôt qu'une date",
"seed_reminder" : "Merci d'écrire votre seed au cas où vous perdriez ou effaceriez votre téléphone",
"seed_title" : "Seed",
"seed_share" : "Partager le seed",
"copy" : "Copier",
"seed_language_choose" : "Merci de choisir la langue du seed :",
"seed_choose" : "Choisissez la langue du seed",
"seed_language_next" : "Suivant",
"seed_language_english" : "Anglais",
"seed_language_chinese" : "Chinois",
"seed_language_dutch" : "Néerlandais",
"seed_language_german" : "Allemand",
"seed_language_japanese" : "Japonais",
"seed_language_portuguese" : "Portugais",
"seed_language_russian" : "Russe",
"seed_language_spanish" : "Espagnol",
"seed_language_french": "Français",
"seed_language_italian": "Italien",
"send_title" : "Envoyer",
"send_your_wallet" : "Votre wallet",
"send_address" : "adresse ${cryptoCurrency}",
"send_payment_id" : "ID de paiement (optionnel)",
"all" : "TOUS",
"send_error_minimum_value" : "La valeur minimale du montant est 0.01",
"send_error_currency" : "La monnaie ne peut contenir que des nombres",
"send_estimated_fee" : "Estimation des frais :",
"send_priority" : "Actuellement les frais sont positionnés à la priorité ${transactionPriority}.\nLa priorité de la transaction peut être modifiée dans les réglages",
"send_creating_transaction" : "Création de la transaction",
"send_templates" : "Gabarits",
"send_new" : "Nouveau",
"send_amount" : "Montant :",
"send_fee" : "Frais :",
"send_name" : "Nom",
"send_got_it" : "Compris",
"send_sending" : "Envoi...",
"send_success" : "Vos ${crypto} ont été envoyés avec succès",
"settings_title" : "Réglages",
"settings_nodes" : "Nœuds",
"settings_current_node" : "Nœud courant",
"settings_wallets" : "Wallets",
"settings_display_balance_as" : "Afficher le solde en",
"settings_currency" : "Monnaie",
"settings_fee_priority" : "Priorité des frais",
"settings_save_recipient_address" : "Sauvegarder l'adresse du bénéficiaire",
"settings_personal" : "Personnel",
"settings_change_pin" : "Modifier le code PIN",
"settings_change_language" : "Modifier la langue",
"settings_allow_biometrical_authentication" : "Autoriser l'authentification biométrique",
"settings_dark_mode" : "Mode sombre",
"settings_transactions" : "Transactions",
"settings_trades" : "Échanges",
"settings_display_on_dashboard_list" : "Afficher sur la liste des tableaux de bord",
"settings_all" : "TOUT",
"settings_only_trades" : "Seulement les échanges",
"settings_only_transactions" : "Seulement les transactions",
"settings_none" : "Rien",
"settings_support" : "Support",
"settings_terms_and_conditions" : "Termes et conditions",
"pin_is_incorrect" : "Le code PIN est incorrect",
"setup_pin" : "Configurer le code PIN",
"enter_your_pin_again" : "Entrez à nouveau votre code PIN",
"setup_successful" : "Votre code PIN a été configuré avec succès !",
"wallet_keys" : "Seed/Clefs du wallet",
"wallet_seed" : "Seed du waller",
"private_key" : "Clef privée",
"public_key" : "Clef publique",
"view_key_private" : "Clef d'audit (view key) (privée)",
"view_key_public" : "Clef d'audit (view key) (publique)",
"spend_key_private" : "Clef de dépense (spend key) (privée)",
"spend_key_public" : "Clef de dépense (spend key) (publique)",
"copied_key_to_clipboard" : "${key} copiée vers le presse-papier",
"new_subaddress_title" : "Nouvelle adresse",
"new_subaddress_label_name" : "Nom",
"new_subaddress_create" : "Créer",
"subaddress_title" : "Liste des sous-adresses",
"trade_details_title" : "Détails de l'échange",
"trade_details_id" : "ID",
"trade_details_state" : "État",
"trade_details_fetching" : "Récupération",
"trade_details_provider" : "Fournisseur",
"trade_details_created_at" : "Créé le",
"trade_details_pair" : "Paire",
"trade_details_copied" : "${title} copié vers le presse-papier",
"trade_history_title" : "Historique des échanges",
"transaction_details_title" : "Détails de transaction",
"transaction_details_transaction_id" : "ID de transaction",
"transaction_details_date" : "Date",
"transaction_details_height" : "Hauteur",
"transaction_details_amount" : "Montant",
"transaction_details_fee" : "Frais",
"transaction_details_copied" : "${title} copié vers le presse-papier",
"transaction_details_recipient_address" : "Adresse du bénéficiaire",
"wallet_list_title" : "Monero Wallet",
"wallet_list_create_new_wallet" : "Créer un Nouveau Wallet",
"wallet_list_restore_wallet" : "Restaurer un Wallet",
"wallet_list_load_wallet" : "Charger wallet",
"wallet_list_loading_wallet" : "Chargement du wallet ${wallet_name}",
"wallet_list_failed_to_load" : "Échec de chargement du wallet ${wallet_name}. ${error}",
"wallet_list_removing_wallet" : "Suppression du wallet ${wallet_name}",
"wallet_list_failed_to_remove" : "Échec de la suppression du wallet ${wallet_name}. ${error}",
"widgets_address" : "Adresse",
"widgets_restore_from_blockheight" : "Restaurer depuis une hauteur de bloc",
"widgets_restore_from_date" : "Restaurer depuis une date",
"widgets_or" : "ou",
"widgets_seed" : "Seed",
"router_no_route" : "Aucune route définie pour ${name}",
"error_text_account_name" : "Le nom de compte ne peut contenir que des lettres et des chiffres\net sa longueur doit être comprise entre 1 et 15 caractères",
"error_text_contact_name" : "Un nom de contact ne peut pas contenir les symboles ` , ' \"\net doit faire entre 1 et 32 caractères",
"error_text_address" : "L'adresse de wallet doit correspondre au type de\ncryptomonnaie",
"error_text_node_address" : "Merci d'entrer une adresse IPv4",
"error_text_node_port" : "Le port d'un nœud doit être un nombre compris entre 0 et 65535",
"error_text_payment_id" : "Un ID de paiement ne peut être constitué que de 16 à 64 caractères hexadécimaux",
"error_text_xmr" : "La valeur de XMR dépasse le solde disponible.\nLa partie décimale doit comporter au plus 12 chiffres",
"error_text_fiat" : "La valeur du montant ne peut dépasser le solde disponible.\nLa partie décimale doit comporter au plus 2 chiffres",
"error_text_subaddress_name" : "Le nom de sous-adresse ne peut contenir les symboles ` , ' \"\net sa longueur doit être comprise entre 1 et 20 caractères",
"error_text_amount" : "Le montant ne peut comporter que des nombres",
"error_text_wallet_name" : "Le nom du wallet ne peut contenir que des lettres et des chiffres\net sa longueur doit être comprise entre 1 et 15 caractères",
"error_text_keys" : "Les clefs du wallet ne peuvent être constituées que de 64 caractères hexadécimaux",
"error_text_crypto_currency" : "La partie décimale\ndoit comporter au plus 12 chiffres",
"error_text_minimal_limit" : "Échange pour ${provider} non créé. Le montant est inférieur au minimum : ${min} ${currency}",
"error_text_maximum_limit" : "Échange pour ${provider} non créé. Le montant est supérieur au maximum : ${max} ${currency}",
"error_text_limits_loading_failed" : "Échange pour ${provider} non créé. Échec de chargement des limites",
"error_text_template" : "Le nom du gabarit et l'adresse ne peuvet pas contenir les symboles ` , ' \"\net leur longueur doit être comprise entre 1 et 106 caractères",
"auth_store_ban_timeout" : "délai de bannisement",
"auth_store_banned_for" : "Banni pour ",
"auth_store_banned_minutes" : " minutes",
"auth_store_incorrect_password" : "Mauvais code PIN",
"wallet_store_monero_wallet" : "Wallet Monero",
"wallet_restoration_store_incorrect_seed_length" : "Longueur de seed incorrecte",
"full_balance" : "Solde Complet",
"available_balance" : "Solde Disponible",
"hidden_balance" : "Solde Caché",
"sync_status_syncronizing" : "SYNCHRONISATION EN COURS",
"sync_status_syncronized" : "SYNCHRONISÉ",
"sync_status_not_connected" : "NON CONNECTÉ",
"sync_status_starting_sync" : "DÉBUT DE SYNCHRO",
"sync_status_failed_connect" : "DÉCONNECTÉ",
"sync_status_connecting" : "CONNEXION EN COURS",
"sync_status_connected" : "CONNECTÉ",
"transaction_priority_slow" : "Lent",
"transaction_priority_regular" : "Normal",
"transaction_priority_medium" : "Moyen",
"transaction_priority_fast" : "Rapide",
"transaction_priority_fastest" : "Le plus rapide",
"trade_for_not_created" : "L'échange pour ${title} n'est pas créé.",
"trade_not_created" : "Échange non créé.",
"trade_id_not_found" : "Échange ${tradeId} de ${title} introuvable.",
"trade_not_found" : "Échange introuvable.",
"trade_state_pending" : "En cours",
"trade_state_confirming" : "En cours de confirmation",
"trade_state_trading" : "Échange en cours",
"trade_state_traded" : "Échange terminé",
"trade_state_complete" : "Finalisé",
"trade_state_to_be_created" : "À créer",
"trade_state_unpaid" : "Non payé",
"trade_state_underpaid" : "Sous payé",
"trade_state_paid_unconfirmed" : "Payé mais non confirmé",
"trade_state_paid" : "Payé",
"trade_state_btc_sent" : "Btc envoyés",
"trade_state_timeout" : "Délai dépassé",
"trade_state_created" : "Créé",
"trade_state_finished" : "Terminé",
"change_language" : "Changer de langue",
"change_language_to" : "Changer la langue vers ${language}?",
"paste" : "Coller",
"restore_from_seed_placeholder" : "Merci d'entrer ou de coller votre seed ici",
"add_new_word" : "Ajouter un nouveau mot",
"incorrect_seed" : "Le texte entré est invalide.",
"biometric_auth_reason" : "Scannez votre empreinte digitale pour vous authentifier",
"version" : "Version ${currentVersion}",
"openalias_alert_title" : "Adresse Détectée",
"openalias_alert_content" : "Vous allez envoyer des fonds à\n${recipient_name}",
"card_address" : "Adresse :",
"buy" : "Acheter",
"sell": "Vendre",
"placeholder_transactions" : "Vos transactions apparaîtront ici",
"placeholder_contacts" : "Vos contacts apparaîtront ici",
"template" : "Gabarit",
"confirm_delete_template" : "Cette action va supprimer ce gabarit. Souhaitez-vous continuer ?",
"confirm_delete_wallet" : "Cette action va supprimer ce wallet. Souhaitez-vous contnuer ?",
"picker_description" : "Pour choisir ChangeNOW ou MorphToken, merci de modifier d'abord la paire de votre échange",
"change_wallet_alert_title" : "Changer le wallet courant",
"change_wallet_alert_content" : "Souhaitez-vous changer le wallet courant vers ${wallet_name}?",
"creating_new_wallet" : "Création d'un nouveau wallet",
"creating_new_wallet_error" : "Erreur : ${description}",
"seed_alert_title" : "Attention",
"seed_alert_content" : "Le seed est la seule façon de restaurer votre wallet. L'avez-vous correctement écrit ?",
"seed_alert_back" : "Retour",
"seed_alert_yes" : "Oui, je suis sûr",
"exchange_sync_alert_content" : "Merci d'attendre que votre wallet soit synchronisé",
"pre_seed_title" : "IMPORTANT",
"pre_seed_description" : "Sur la page suivante vous allez voir une série de ${words} mots. Ils constituent votre seed unique et privé et sont le SEUL moyen de restaurer votre wallet en cas de perte ou de dysfonctionnement. Il est de VOTRE responsabilité d'écrire cette série de mots et de les stocker dans un lieu sûr en dehors de l'application Cake Waller.",
"pre_seed_button_text" : "J'ai compris. Montrez moi mon seed",
"xmr_to_error" : "Erreur XMR.TO",
"xmr_to_error_description" : "Montant invalide. La partie décimale doit contenir au plus 8 chiffres",
"provider_error" : "Errur de ${provider}",
"use_ssl" : "Utiliser SSL",
"color_theme" : "Thème",
"light_theme" : "Clair",
"bright_theme" : "Vif",
"dark_theme" : "Sombre",
"enter_your_note" : "Entrez votre note…",
"note_optional" : "Note (optionnelle)",
"note_tap_to_change" : "Note (appuyez pour changer)",
"transaction_key" : "Clef de Transaction",
"confirmations" : "Confirmations",
"recipient_address" : "Adresse bénéficiaire",
"extra_id" : "ID supplémentaire :",
"destination_tag" : "Tag de destination :",
"memo" : "Mémo :",
"backup" : "Sauvegarde",
"change_password" : "Changer le mot de passe",
"backup_password" : "Mot de passe de sauvegarde",
"write_down_backup_password" : "Merci d'écrire votre mot de passe de sauvegarde, vous en aurez besoin lors de la relecture de vos fichiers de sauvegarde.",
"export_backup" : "Exporter la sauvegarde",
"save_backup_password" : "Please make sure that you have saved your backup password. You will not be able to import your backup files without it.",
"backup_file" : "Fichier de sauvegarde",
"edit_backup_password" : "Modifier le Mot de Passe de Sauvegarde",
"save_backup_password_alert" : "Enregistrer le mot de passe de sauvegarde",
"change_backup_password_alert" : "Vos fichiers de sauvegarde précédents ne pourront pas être importés avec le nouveau mot de passe de sauvegarde. Le nouveau mot de passe ne sera utilisé que pour les nouveaux fichiers de sauvegarde. Êtes vous certain de vouloir modifier le mot de passe de sauvegarde ?",
"enter_backup_password" : "Entrez le mot de passe de sauvegarde ici",
"select_backup_file" : "Sélectionnez le fichier de sauvegarde",
"import" : "Importer",
"please_select_backup_file" : "Merci de sélectionner le fichier de sauvegarde et de rentrer le mot de passe associé.",
"fixed_rate" : "Taux fixe",
"fixed_rate_alert" : "Vous aurez la possibilité de rentrer le montant reçu lorsque le mode taux fixe est sélectionné. Souhaitez vous basculer en mode taux fixe ?",
"xlm_extra_info" : "Merci de ne pas oublier de spécifier l'ID de mémo lors de l'envoi de la transaction XLM de l'échange",
"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 wallet Monero au préalable.",
"confirmed" : "Confirmé",
"unconfirmed" : "Non confirmé",
"displayable" : "Affichable",
"submit_request" : "soumettre une requête",
"buy_alert_content" : "Pour le moment nous ne supportons que l'achat de Bitcoin et Litecoin. Pour acheter du Bitcoin ou du Litecoin, merci de créer ou de sélectionner votre wallet Bitcoin ou Litecoin.",
"sell_alert_content": "Pour le moment nous ne supportons que la vente de Bitcoin. Pour vendre du Bitcoin, merci de créer ou de sélectionner votre wallet Bitcoint.",
"outdated_electrum_wallet_description" : "Les nouveaux wallet Bitcoin créés dans Cake ont dorénavant un seed de 24 mots. Il est impératif que vous créiez un nouveau wallet Bitcoin, que vous y transfériez tous vos fonds puis que vous cessiez d'utiliser le wallet avec un seed de 12 mots. Merci de faire cela immédiatement pour assurer la sécurité de vos avoirs.",
"understand" : "J'ai compris",
"apk_update" : "Mise à jour d'APK",
"buy_bitcoin" : "Acheter du Bitcoin",
"buy_with" : "Acheter avec",
"moonpay_alert_text" : "Le montant doit être au moins égal à ${minAmount} ${fiatCurrency}",
"outdated_electrum_wallet_receive_warning": "Si ce wallet a un seed de 12 mots et a été créé dans Cake, NE PAS y déposer de Bitcoin. Tous les BTC transférés vers ce wallet seront perdus. Créez un nouveau wallet avec seed de 24 mots (appuyez sur le menu en haut à droite, sélectionnez Wallets puis Créer un Nouveau Wallet et enfin Bitcoin) et transférez y IMMÉDIATEMENT vos BTC. Les nouveaux wallets BTC Cake (avec seed de 24 mots) New (24-word) sont sécurisés",
"do_not_show_me": "Ne plus me montrer ceci à l'avenir",
"unspent_coins_title" : "Pièces (coins) non dépensées",
"unspent_coins_details_title" : "Détails des pièces (coins) non dépensées",
"freeze" : "Geler",
"frozen" : "Gelées",
"coin_control" : "Contrôle des pièces (optionnel)",
"address_detected" : "Adresse détectée",
"address_from_domain" : "Cette adresse est issue de ${domain} sur Unstoppable Domains",
"add_receiver" : "Ajouter un autre bénéficiaire (optionnel)",
"manage_yats" : "Gérer les Yats",
"yat_alert_title" : "Envoyez et recevez des cryptos plus facilement avec Yat",
"yat_alert_content" : "Les utilisateurs de Cake Wallet peuvent maintenant envoyer et recevoir leurs monnaies favories avec un utilisateur unique en son genre basé sur les emojis.",
"get_your_yat" : "Obtenez votre Yat",
"connect_an_existing_yat" : "Connectez un Yat existant",
"connect_yats": "Connecter Yats",
"yat_address" : "Adresse Yat",
"yat" : "Yat",
"address_from_yat" : "Cette adresse est issue de ${emoji} sur Yat",
"yat_error" : "Erreur Yat",
"yat_error_content" : "Aucune adresse associée à ce Yat. Essayez un autre Yat",
"choose_address" : "\n\nMerci de choisir l'adresse :",
"yat_popup_title" : "L'adresse de votre wallet be êyre emojifiée.",
"yat_popup_content" : "Vous pouvez à présent envoyer et recevoir des cryptos dans Cake Wallet à l'aide de votre Yat - un nom d'utilisateur court à base d'emoji. Gérér les Yats à tout moment depuis l'écran de paramétrage",
"second_intro_title" : "Une adresse emoji pour les gouverner tous",
"second_intro_content" : "Votre Yat est une seule et unique adresse emoji qui remplace toutes vos longues adresses hexadécimales pour toutes vos cryptomonnaires.",
"third_intro_title" : "Yat joue bien avec les autres",
"third_intro_content" : "Les Yats existent aussi en dehors de Cake Wallet. Toute adresse sur terre peut être remplacée par un Yat !",
"learn_more" : "En savoir plus",
"new_template" : "Nouveau Gabarit",
"electrum_address_disclaimer": "Nous générons de nouvelles adresses à chaque fois que vous en utilisez une, mais les adresses précédentes continuent à fonctionner"
}